diff --git a/ChangeLog b/ChangeLog index e9b12ddf9..2bd404e99 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Wed Mar 4 10:32:40 1998 Werner Koch (wk@isil.d.shuttle.de) + + * configure.in (getrusage,gettimeofday): New tests. + Fri Feb 27 13:14:17 1998 Werner Koch (wk@isil.d.shuttle.de) * configure.in (--disable-m-guard): New. diff --git a/NEWS b/NEWS index 4ad308d20..609c16322 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,17 @@ +Noteworthy changes in version 0.2.12 +------------------------------------ + + * --delete-key checks that there is no secret key. The new + option --delete-secret-key maybe used to delete a secret key. + + * "-kv" now works as expected. Options "--list-{keys,sigs]" + and "--check-sigs" are now working. + + * New options "--verify" and "--decrypt" to better support integration + into MUAs (partly done for Mutt). + + * New option "--with-colons" to make parsing of key lists easier. + Noteworthy changes in version 0.2.11 ------------------------------------ diff --git a/README b/README index 5815fc1c0..664c8a320 100644 --- a/README +++ b/README @@ -3,8 +3,6 @@ ------------------------------- THIS IS ALPHA SOFTWARE, EXPECT BUGS AND UNIMPLEMENTED STUFF. - IT MAY HAPPEN THAT SOME DATA FORMATS OR PROGRAMM OPTIONS - CHANGE WITH THE NEXT VERSION. On a Linux box (version 2.x.x, alpha or x86 CPU) it should work reliable. You may create your key on such a machine and @@ -105,7 +103,7 @@ 8) Continue with step 4 if we did not find a prime in step 7. 9) Find a generator for that prime. - You should make a revocation certificate in cases someone gets + You should make a revocation certificate in case someone gets knowledge of your secret key or you forgot your passphrase: gpg --gen-revoke your_user_id @@ -208,12 +206,14 @@ "234567C4" "0F34E556E" "01347A56A" + "0xAB123456 * By a complete keyid: "234AABBCC34567C4" "0F323456784E56EAB" "01AB3FED1347A5612" + "0x234AABBCC34567C4" * By a fingerprint (not yet implemented): @@ -280,11 +280,6 @@ you are asked for the passphrase, so that GNUPG is able to look at the inner structure of a encrypted packet. - gpgm --quick-random - - Do not use the stroing random generator but a faster one. This can be - used to generate keys for tests; those are marked as insecure. - gpgm --list-trustdb List the contents of the trustdb in a human readable format diff --git a/THANKS b/THANKS index eb5a62cba..4d0436512 100644 --- a/THANKS +++ b/THANKS @@ -1,4 +1,4 @@ -G10 has originally been written by Werner Koch. Other people contributed +GNUPG has originally been written by Werner Koch. Other people contributed by reporting problems, suggesting various improvements or submitting actual code. Here is a list of these people. Help me keeping it complete and exempt of errors. diff --git a/TODO b/TODO index 6edd5d523..e8fabe51d 100644 --- a/TODO +++ b/TODO @@ -13,9 +13,7 @@ * complete cipher/cast.c * complete cipher/dsa.c - * add g10 stuff to Mutt's pgpinvoke.c - - * Burn the buffers used by fopen(). + * Burn the buffers used by fopen(), or use read(2). * bug: g10/trustdb.c#build_sigrecs called to often by do_list_path and remove the bad kludge. Maybe we should put all sigs into the trustdb @@ -34,4 +32,11 @@ them to your key and because the user id which is signed by others has also be signed by you, all user-ids are bound together. + * add an option to re-create a public key from a secret key + + * should we have a simple menu for all the key management options? + + * cleanup mainproc.c, much stuff is duplicated. + + * remove key management stuff from sign.c. diff --git a/VERSION b/VERSION index d3b5ba4bf..c7ea8e05a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.11 +0.2.12a diff --git a/cipher/ChangeLog b/cipher/ChangeLog index 152d23412..0a316d1bb 100644 --- a/cipher/ChangeLog +++ b/cipher/ChangeLog @@ -1,3 +1,18 @@ +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. diff --git a/cipher/Makefile.am b/cipher/Makefile.am index 48478a21d..c0b3c8a5b 100644 --- a/cipher/Makefile.am +++ b/cipher/Makefile.am @@ -13,6 +13,7 @@ libcipher_a_SOURCES = blowfish.c \ md5.c \ md5.h \ primegen.c \ + random.h \ random.c \ rmd.h \ rmd160.c \ diff --git a/cipher/Makefile.in b/cipher/Makefile.in index 958b7d167..579d4a355 100644 --- a/cipher/Makefile.in +++ b/cipher/Makefile.in @@ -104,6 +104,7 @@ libcipher_a_SOURCES = blowfish.c \ md5.c \ md5.h \ primegen.c \ + random.h \ random.c \ rmd.h \ rmd160.c \ diff --git a/cipher/blowfish.c b/cipher/blowfish.c index c0979b8ce..55b99025c 100644 --- a/cipher/blowfish.c +++ b/cipher/blowfish.c @@ -37,6 +37,7 @@ #include "util.h" #include "types.h" #include "blowfish.h" +#include "random.h" /* precomputed S boxes */ static const u32 ks0[256] = { @@ -421,6 +422,8 @@ blowfish_setkey( BLOWFISH_context *c, byte *key, unsigned keylen ) selftest(); } + fast_random_poll(); + for(i=0; i < BLOWFISH_ROUNDS+2; i++ ) c->p[i] = ps[i]; for(i=0; i < 256; i++ ) { diff --git a/cipher/dsa.c b/cipher/dsa.c index 07f9e7075..8024ac0a2 100644 --- a/cipher/dsa.c +++ b/cipher/dsa.c @@ -32,6 +32,7 @@ void dsa_free_public_key( DSA_public_key *pk ) { mpi_free( pk->p ); pk->p = NULL; + mpi_free( pk->q ); pk->q = NULL; mpi_free( pk->g ); pk->g = NULL; mpi_free( pk->y ); pk->y = NULL; } @@ -40,121 +41,13 @@ void dsa_free_secret_key( DSA_secret_key *sk ) { mpi_free( sk->p ); sk->p = NULL; + mpi_free( sk->q ); sk->q = NULL; mpi_free( sk->g ); sk->g = NULL; mpi_free( sk->y ); sk->y = NULL; mpi_free( sk->x ); sk->x = NULL; } -static void -test_keys( DSA_public_key *pk, DSA_secret_key *sk, unsigned nbits ) -{ - MPI test = mpi_alloc( nbits / BITS_PER_MPI_LIMB ); - 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 ); - - mpi_set_bytes( test, nbits, get_random_byte, 0 ); - - dsa_sign( out1_a, out1_b, test, sk ); - if( !dsa_verify( out1_a, out1_b, test, pk ) ) - log_fatal("DSA 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( mpi_get_nlimbs(p) ); - MPI temp = mpi_alloc( mpi_get_nlimbs(p) ); - MPI p_1 = mpi_copy(p); - unsigned nbits = mpi_get_nbits(p); - - if( DBG_CIPHER ) - log_debug("choosing a random k "); - mpi_sub_ui( p_1, p, 1); - for(;;) { - if( DBG_CIPHER ) - fputc('.', stderr); - mpi_set_bytes( k, nbits, get_random_byte, 1 ); - mpi_set_highbit( k, nbits-1 ); /* make sure it's high (really needed?) */ - if( mpi_cmp( k, p_1 ) >= 0 ) - continue; /* is not smaller than (p-1) */ - if( mpi_gcd( temp, k, p_1 ) ) - break; /* okay, k is relatively prime to (p-1) */ - } - if( DBG_CIPHER ) - fputc('\n', stderr); - 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 - */ -void -dsa_generate( DSA_public_key *pk, DSA_secret_key *sk, unsigned nbits ) -{ - MPI p; /* the prime */ - MPI g; - MPI x; /* the secret exponent */ - MPI y; - - p = generate_public_prime( nbits ); - /* FIXME: check wether we shall assert that (p-1)/2 is also prime - * Schneier votes against it - */ - g = mpi_alloc_set_ui(3); - - /* select a random number */ - x = mpi_alloc_secure( nbits/BITS_PER_MPI_LIMB ); - if( DBG_CIPHER ) - log_debug("choosing a random x "); - do { - if( DBG_CIPHER ) - fputc('.', stderr); - mpi_set_bytes( x, nbits, get_random_byte, 1 ); /* fixme: should be 2 */ - mpi_set_highbit( x, nbits-1 ); /* make sure it's high (needed?) */ - } while( mpi_cmp( x, p ) >= 0 ); /* x must be smaller than p */ - - y = mpi_alloc(nbits/BITS_PER_MPI_LIMB); - mpi_powm( y, g, x, p ); - - if( DBG_CIPHER ) { - fputc('\n', stderr); - log_mpidump("dsa p= ", p ); - log_mpidump("dsa g= ", g ); - log_mpidump("dsa y= ", y ); - log_mpidump("dsa x= ", x ); - } - - - /* copy the stuff to the key structures */ - pk->p = mpi_copy(p); - pk->g = mpi_copy(g); - pk->y = mpi_copy(y); - sk->p = p; - sk->g = g; - sk->y = y; - sk->x = x; - - /* now we can test our keys (this should never fail!) */ - test_keys( pk, sk, nbits - 64 ); -} - - /**************** * Test wether the secret key is valid. * Returns: if this is a valid key. @@ -174,72 +67,58 @@ dsa_check_secret_key( DSA_secret_key *sk ) /**************** - * Make an Elgamal signature out of INPUT + * Make a DSA signature out of INPUT */ void -dsa_sign(MPI a, MPI b, MPI input, DSA_secret_key *skey ) +dsa_sign(MPI r, MPI s, MPI input, DSA_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("dsa sign p= ", skey->p); - log_mpidump("dsa sign g= ", skey->g); - log_mpidump("dsa sign y= ", skey->y); - log_mpidump("dsa sign x= ", skey->x); - log_mpidump("dsa sign k= ", k); - log_mpidump("dsa sign M= ", input); - log_mpidump("dsa sign a= ", a); - log_mpidump("dsa sign b= ", b); - } - #endif - mpi_free(k); - mpi_free(t); - mpi_free(inv); - mpi_free(p_1); } /**************** - * Returns true if the signature composed from A and B is valid. + * Returns true if the signature composed from R and S is valid. */ int -dsa_verify(MPI a, MPI b, MPI input, DSA_public_key *pkey ) +dsa_verify(MPI r, MPI s, MPI input, DSA_public_key *pkey ) { int rc; - MPI t1 = mpi_alloc( mpi_get_nlimbs(a) ); - MPI t2 = mpi_alloc( mpi_get_nlimbs(a) ); + MPI w, u1, u2, v; + MPI base[3]; + MPI exp[3]; - mpi_powm( t1, pkey->y, a, pkey->p ); - mpi_powm( t2, a, b, pkey->p ); - mpi_mulm( t1, t1, t2, pkey->p ); + 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 */ - mpi_powm( t2, pkey->g, input, pkey->p ); + 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) ); - rc = !mpi_cmp( t1, t2 ); + /* w = s^(-1) mod q */ + mpi_invm( w, s, pkey->q ); - mpi_free(t1); - mpi_free(t2); + /* u1 = (input * w) mod q */ + mpi_mulm( u1, input, 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; } diff --git a/cipher/dsa.h b/cipher/dsa.h index 1574560f6..07a41ae6c 100644 --- a/cipher/dsa.h +++ b/cipher/dsa.h @@ -43,7 +43,7 @@ void dsa_free_public_key( DSA_public_key *pk ); void dsa_free_secret_key( DSA_secret_key *sk ); void dsa_generate( DSA_public_key *pk, DSA_secret_key *sk, unsigned nbits ); int dsa_check_secret_key( DSA_secret_key *sk ); -void dsa_sign(MPI a, MPI b, MPI input, DSA_secret_key *skey); -int dsa_verify(MPI a, MPI b, MPI input, DSA_public_key *pkey); +void dsa_sign(MPI r, MPI s, MPI input, DSA_secret_key *skey); +int dsa_verify(MPI r, MPI s, MPI input, DSA_public_key *pkey); #endif /*G10_DSA_H*/ diff --git a/cipher/md.c b/cipher/md.c index a9a566698..b19399653 100644 --- a/cipher/md.c +++ b/cipher/md.c @@ -42,6 +42,7 @@ md_open( int algo, int secure ) hd->secure = secure; if( algo ) md_enable( hd, algo ); + fast_random_poll(); return hd; } diff --git a/cipher/misc.c b/cipher/misc.c index 937aa61f5..ad937fcfe 100644 --- a/cipher/misc.c +++ b/cipher/misc.c @@ -174,6 +174,7 @@ check_pubkey_algo( int algo ) { switch( algo ) { case PUBKEY_ALGO_ELGAMAL: + case PUBKEY_ALGO_DSA: #ifdef HAVE_RSA_CIPHER case PUBKEY_ALGO_RSA: #endif diff --git a/cipher/random.c b/cipher/random.c index 2f11df7cd..5a6371995 100644 --- a/cipher/random.c +++ b/cipher/random.c @@ -18,6 +18,17 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ + +/**************** + * How it works: + * + * See Peter Gutmann's Paper: "Software Generation of Practically + * Strong Random Numbers" + * + * fixme! + */ + + #include #include #include @@ -26,27 +37,88 @@ #include #include #include +#ifndef HAVE_GETTIMEOFTIME + #include +#endif +#ifdef HAVE_GETRUSAGE + #include +#endif #include #include #include #include "util.h" -#include "cipher.h" +#include "random.h" +#include "rmd.h" #include "ttyio.h" #include "i18n.h" + +#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) +#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 + struct cache { int len; - byte buffer[100]; /* fixme: should be allocated with m_alloc_secure()*/ + int size; + byte *buffer; }; + +static int is_initialized; static struct cache cache[3]; #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 just_mixed; - -static void fill_buffer( byte *buffer, size_t length, int level ); +static int secure_alloc; static int quick_test; + +static void read_pool( byte *buffer, size_t length, int level ); +static void read_dev_random( byte *buffer, size_t length, int level ); + + +static void +initialize() +{ + /* 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; +} + +void +secure_random_alloc() +{ + secure_alloc = 1; +} + int quick_random_gen( int onoff ) { @@ -78,14 +150,174 @@ get_random_byte( int level ) { MASK_LEVEL(level); if( !cache[level].len ) { - fill_buffer(cache[level].buffer, DIM(cache[level].buffer), level ); - cache[level].len = DIM(cache[level].buffer); + if( !is_initialized ) + initialize(); + if( !cache[level].buffer ) { + cache[level].size = 100; + cache[level].buffer = level && secure_alloc? + m_alloc_secure( cache[level].size ) + : m_alloc( cache[level].size ); + } + read_pool(cache[level].buffer, cache[level].size, level ); + cache[level].len = cache[level].size; } return cache[level].buffer[--cache[level].len]; } +/**************** + * 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 */ + if( !level ) { /* read simple random bytes */ + read_dev_random( buffer, length, level ); + return; + } + + /* always do a random poll if we need strong numbers */ + if( pool_filled && level == 2 ) + random_poll(); + /* make sure the pool is filled */ + while( !pool_filled ) + random_poll(); + /* do always a fast random poll */ + fast_random_poll(); + + /* mix the pool (if add_randomness() didn't it) */ + if( !just_mixed ) + mix_pool(rndpool); + + /* 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); + /* 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; + } + /* 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 randomeness source. + */ +void +add_randomness( const void *buffer, size_t length, int source ) +{ + if( !is_initialized ) + initialize(); + while( length-- ) { + rndpool[pool_writepos++] = *((byte*)buffer)++; + if( pool_writepos >= POOLSIZE ) { + pool_filled = 1; + pool_writepos = 0; + mix_pool(rndpool); + just_mixed = !length; + } + } +} + + + +/******************** + * FIXME: move these functions to rand_unix.c + */ + +void +random_poll() +{ + char buf[POOLSIZE/5]; + read_dev_random( buf, POOLSIZE/5, 1 ); /* read /dev/urandom */ + add_randomness( buf, POOLSIZE/5, 2); + memset( buf, 0, POOLSIZE/5); +} + + +void +fast_random_poll() +{ + #ifdef HAVE_GETTIMEOFTIME + { 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 */ + { struct tms buf; + times( &buf ); + add_randomness( &buf, sizeof buf, 1 ); + } + #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 +} + #ifdef HAVE_DEV_RANDOM @@ -111,7 +343,7 @@ open_device( const char *name, int minor ) static void -fill_buffer( byte *buffer, size_t length, int level ) +read_dev_random( byte *buffer, size_t length, int level ) { static int fd_urandom = -1; static int fd_random = -1; @@ -125,6 +357,9 @@ fill_buffer( byte *buffer, size_t length, int level ) fd = fd_random; } else { + /* fixme: we should use a simpler one for level 0, + * because reading from /dev/urandom removes entropy + * and the next read on /dev/random may have to wait */ if( fd_urandom == -1 ) fd_urandom = open_device( "/dev/urandom", 9 ); fd = fd_urandom; @@ -154,7 +389,7 @@ fill_buffer( byte *buffer, size_t length, int level ) continue; } - assert( length < 200 ); + assert( length < 500 ); do { n = read(fd, buffer, length ); if( n >= 0 && n > length ) { @@ -178,7 +413,7 @@ fill_buffer( byte *buffer, size_t length, int level ) #endif static void -fill_buffer( byte *buffer, size_t length, int level ) +read_dev_random( byte *buffer, size_t length, int level ) { static int initialized=0; diff --git a/cipher/random.h b/cipher/random.h new file mode 100644 index 000000000..a8c506da7 --- /dev/null +++ b/cipher/random.h @@ -0,0 +1,39 @@ +/* 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 secure_random_alloc(void); +int quick_random_gen( int onoff ); +void randomize_buffer( byte *buffer, size_t length, int level ); +byte get_random_byte( int level ); +void add_randomness( const void *buffer, size_t length, int source ); + + +/*-- the next two functions are implemented by all the system + specific source files rand_xxxx.s --*/ +void random_poll(void); +void fast_random_poll(void); + + +#endif /*G10_RANDOM_H*/ diff --git a/cipher/rmd.h b/cipher/rmd.h index d6bc007d1..f3a67243b 100644 --- a/cipher/rmd.h +++ b/cipher/rmd.h @@ -33,6 +33,7 @@ typedef struct { void rmd160_init( RMD160_CONTEXT *c ); void rmd160_write( RMD160_CONTEXT *hd, byte *inbuf, size_t inlen); void rmd160_final(RMD160_CONTEXT *hd); +void rmd160_mixblock( RMD160_CONTEXT *hd, char *buffer ); #define rmd160_read(h) ( (h)->buf ) #endif /*G10_RMD_H*/ diff --git a/cipher/rmd160.c b/cipher/rmd160.c index 7b87d4218..8d17b287f 100644 --- a/cipher/rmd160.c +++ b/cipher/rmd160.c @@ -300,6 +300,27 @@ rmd160_write( RMD160_CONTEXT *hd, byte *inbuf, size_t inlen) } +/**************** + * Apply the rmd160 transform function on the buffer which must have + * a length 64 bytes. Do not use this function together with the + * other functions, use rmd160_init to initialize intzernal variables. + * Returns: 16 bytes in buffer with the mixed contentes of buffer. + */ +void +rmd160_mixblock( RMD160_CONTEXT *hd, char *buffer ) +{ + char *p = buffer; + transform( hd, buffer ); + #define X(a) do { *(u32*)p = hd->h##a ; p += 4; } while(0) + X(0); + X(1); + X(2); + X(3); + X(4); + #undef X +} + + /* The routine terminates the computation */ diff --git a/config.h.in b/config.h.in index 95f2175f8..485605b18 100644 --- a/config.h.in +++ b/config.h.in @@ -142,6 +142,12 @@ /* Define if you have the getpagesize function. */ #undef HAVE_GETPAGESIZE +/* Define if you have the getrusage function. */ +#undef HAVE_GETRUSAGE + +/* Define if you have the gettimeofday function. */ +#undef HAVE_GETTIMEOFDAY + /* Define if you have the mlock function. */ #undef HAVE_MLOCK diff --git a/configure.in b/configure.in index 660f54144..43ca5a188 100644 --- a/configure.in +++ b/configure.in @@ -133,6 +133,7 @@ fi dnl Checks for library functions. AC_FUNC_VPRINTF AC_CHECK_FUNCS(strerror stpcpy strlwr tcgetattr rand strtoul mlock mmap) +AC_CHECK_FUNCS(gettimeofday getrusage) diff --git a/doc/gpg.1pod b/doc/gpg.1pod index ecbc39138..da755256f 100644 --- a/doc/gpg.1pod +++ b/doc/gpg.1pod @@ -32,13 +32,30 @@ B<-c>, B<--symmetric> This command asks for a passphrase. B<--store> - store only (make a RFC1991 packet). + store only (make a simple RFC1991 packet). -B<-d>, B<--decrypt> - Decrypt data. This is the default operation for data - files. +B<--decrypt> [I] + Decrypt file (or stdin if no file is specified) and + write it to stdout (or the file specified with + B<--output>). If the decrypted file is signed, the + signature is also verified. This command differs + from the default operation, as it never write to the + filename which is included in the file and that it + rejects files which don't begin with an encrypted + message. -B<-k> [I] +B<--verify> [[I] {I}] + Assume that I is a signature and verify it + without generating any output. With no arguments, + the signature packet is read from stdin (it may be a + detached signature when not used in batch mode). If + only a sigfile is given, is maybe a complete signature + or a detached signature in which case the signed stuff + is expected from stdin. With more than 1 argument, the + first should be a detached signature and the remaining + files are the signed stuff. + +B<-k> [I] [I] Kludge to be somewhat compatibe to PGP. Without arguments, all public key-rings are listed, with one argument, only I is listed. @@ -50,15 +67,21 @@ B<-k> [I] B<-kvc> List fingerprints B<-kvvc> List fingerprints and signatures -B<--list-keys> - List all keys in all public key-rings and check the - signatures. +B<--list-keys> [I] + List all keys from the default public keyring or just the ones + given on the commandline. -B<--check-keys> - Check signatures on a key in the keyring +B<--list-sigs> [I] + Same as B<--list-keys>, but the signatures are listed too. -B<--fingerprint> - Show the fingerprints +B<--check-sigs> [I] + Same as B<--list-sigs>, but the signatures are verified. + +B<--fingerprint> [I] + List all keys with their fingerprints. This is the + same output as B but with the additonal output + of a line with the fingerprint. May also be combined + with B<--list-sigs> or B<--check-sigs>. B<--list-packets> List only the sequence of packets. This is mainly @@ -84,7 +107,10 @@ B<--sign-key> I B<--delete-key> Remove key from the public keyring -B<--edit-sig> +B<--delete-secret-key> + Remove key from the secret and public keyring + +B<--edit-key> Edit/remove a key signature. B<--change-passphrase> @@ -124,9 +150,13 @@ B<-o> I, B<--output> I B<-u> I, B<--local-user> I Use I as the user-id to sign. + This option is silently ignored for the list commands, + so that it can be used in an options file. B<-r> I, B<--remote-user> I - Use I as the user-id for encryption. + Use I as the user-id for encryption. + This option is silently ignored for the list commands, + so that it can be used in an options file. B<-v>, B<--verbose> Give more informations during processing. If used @@ -181,7 +211,7 @@ B<--options> I (see B<--homedir>). This option is ignored when used in an options file. -B +B<--no-options> Shortcut for B<--options> I. This option is detected before an attempt to open an option file. @@ -227,17 +257,17 @@ B<--passphrase-fd> I can only be used if only one passphrase is supplied. B -B +B<--no-verbose> Reset verbose level to 0. -B +B<--no-greeting> Suppress the initial copyright message but do not enter batch mode. -B +B<--no-armor> Assume the input data is not in ASCCI armored format. -B +B<--no-default-keyring> Do not add the default key-rings to the list of key-rings. @@ -245,6 +275,9 @@ B<--version> Print version information along with a list of supported algorithms. +B<--with-colons> + Print key listings delimited by colons. + B<--warranty> Print warranty information. diff --git a/g10/ChangeLog b/g10/ChangeLog index c1f6036c4..c64144117 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,49 @@ +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 diff --git a/g10/Makefile.am b/g10/Makefile.am index aaed0ff23..edfdba412 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -26,6 +26,7 @@ common_source = \ textfilter.c \ cipher.c \ elg.c \ + dsa.c \ rsa.c \ options.h \ openfile.c \ @@ -48,16 +49,18 @@ common_source = \ encr-data.c \ encode.c \ revoke.c \ + keylist.c \ sig-check.c gpg_SOURCES = g10.c \ $(common_source) \ + verify.c \ + decrypt.c \ keygen.c gpgm_SOURCES = g10maint.c \ dearmor.c \ - keylist.c \ $(common_source) LDADD = @INTLLIBS@ $(needed_libs) @ZLIBS@ diff --git a/g10/Makefile.in b/g10/Makefile.in index caffcd7f5..cc4b69b36 100644 --- a/g10/Makefile.in +++ b/g10/Makefile.in @@ -118,6 +118,7 @@ common_source = \ textfilter.c \ cipher.c \ elg.c \ + dsa.c \ rsa.c \ options.h \ openfile.c \ @@ -140,15 +141,17 @@ common_source = \ encr-data.c \ encode.c \ revoke.c \ + keylist.c \ sig-check.c gpg_SOURCES = g10.c \ $(common_source) \ + verify.c \ + decrypt.c \ keygen.c gpgm_SOURCES = g10maint.c \ dearmor.c \ - keylist.c \ $(common_source) LDADD = @INTLLIBS@ $(needed_libs) @ZLIBS@ @@ -164,20 +167,20 @@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ gpg_OBJECTS = g10.o build-packet.o compress.o free-packet.o getkey.o \ pkclist.o skclist.o ringedit.o kbnode.o mainproc.o armor.o mdfilter.o \ -textfilter.o cipher.o elg.o rsa.o openfile.o keyid.o trustdb.o \ +textfilter.o cipher.o elg.o dsa.o rsa.o openfile.o keyid.o trustdb.o \ parse-packet.o passphrase.o pubkey-enc.o seckey-cert.o seskey.o \ import.o export.o comment.o status.o sign.o plaintext.o encr-data.o \ -encode.o revoke.o sig-check.o keygen.o +encode.o revoke.o keylist.o sig-check.o verify.o decrypt.o keygen.o gpg_LDADD = $(LDADD) gpg_DEPENDENCIES = ../cipher/libcipher.a ../mpi/libmpi.a \ ../util/libutil.a gpg_LDFLAGS = -gpgm_OBJECTS = g10maint.o dearmor.o keylist.o build-packet.o compress.o \ +gpgm_OBJECTS = g10maint.o dearmor.o build-packet.o compress.o \ free-packet.o getkey.o pkclist.o skclist.o ringedit.o kbnode.o \ -mainproc.o armor.o mdfilter.o textfilter.o cipher.o elg.o rsa.o \ +mainproc.o armor.o mdfilter.o textfilter.o cipher.o elg.o dsa.o rsa.o \ openfile.o keyid.o trustdb.o parse-packet.o passphrase.o pubkey-enc.o \ seckey-cert.o seskey.o import.o export.o comment.o status.o sign.o \ -plaintext.o encr-data.o encode.o revoke.o sig-check.o +plaintext.o encr-data.o encode.o revoke.o keylist.o sig-check.o gpgm_LDADD = $(LDADD) gpgm_DEPENDENCIES = ../cipher/libcipher.a ../mpi/libmpi.a \ ../util/libutil.a @@ -193,16 +196,16 @@ DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) TAR = tar GZIP = --best DEP_FILES = .deps/armor.P .deps/build-packet.P .deps/cipher.P \ -.deps/comment.P .deps/compress.P .deps/dearmor.P .deps/elg.P \ -.deps/encode.P .deps/encr-data.P .deps/export.P .deps/free-packet.P \ -.deps/g10.P .deps/g10maint .deps/g10maint.P .deps/getkey.P \ -.deps/import.P .deps/kbnode.P .deps/keygen.P .deps/keyid.P \ -.deps/keylist.P .deps/mainproc.P .deps/mdfilter.P .deps/openfile.P \ -.deps/parse-packet.P .deps/passphrase.P .deps/pkclist.P \ -.deps/plaintext.P .deps/pubkey-enc.P .deps/revoke.P .deps/ringedit.P \ -.deps/rsa.P .deps/seckey-cert.P .deps/seskey.P .deps/sig-check.P \ -.deps/sign.P .deps/skclist.P .deps/status.P .deps/textfilter.P \ -.deps/trustdb.P +.deps/comment.P .deps/compress.P .deps/dearmor.P .deps/decrypt.P \ +.deps/dsa.P .deps/elg.P .deps/encode.P .deps/encr-data.P .deps/export.P \ +.deps/free-packet.P .deps/g10.P .deps/g10maint .deps/g10maint.P \ +.deps/getkey.P .deps/import.P .deps/kbnode.P .deps/keygen.P \ +.deps/keyid.P .deps/keylist.P .deps/mainproc.P .deps/mdfilter.P \ +.deps/openfile.P .deps/parse-packet.P .deps/passphrase.P \ +.deps/pkclist.P .deps/plaintext.P .deps/pubkey-enc.P .deps/revoke.P \ +.deps/ringedit.P .deps/rsa.P .deps/seckey-cert.P .deps/seskey.P \ +.deps/sig-check.P .deps/sign.P .deps/skclist.P .deps/status.P \ +.deps/textfilter.P .deps/trustdb.P .deps/verify.P SOURCES = $(gpg_SOURCES) $(gpgm_SOURCES) OBJECTS = $(gpg_OBJECTS) $(gpgm_OBJECTS) diff --git a/g10/armor.c b/g10/armor.c index 142e0bd82..3192e95b3 100644 --- a/g10/armor.c +++ b/g10/armor.c @@ -322,7 +322,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, buf[--n] = 0; if( opt.verbose ) { log_info("armor header: "); - print_string( stderr, buf, n ); + print_string( stderr, buf, n, 0 ); putc('\n', stderr); } if( clearsig && !parse_hash_header( buf ) ) { @@ -348,7 +348,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, } else { log_error("invalid armor header: "); - print_string( stderr, buf, n ); + print_string( stderr, buf, n, 0 ); putc('\n', stderr); state = fhdrERROR; } @@ -357,7 +357,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, if( strchr( buf, ':') ) { /* buffer to short, but this is okay*/ if( opt.verbose ) { log_info("armor header: "); - print_string( stderr, buf, n ); + print_string( stderr, buf, n, 0 ); fputs("[...]\n", stderr); /* indicate it is truncated */ } state = fhdrSKIPHeader; /* skip rest of line */ @@ -462,7 +462,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, } else { log_error("invalid dash escaped line: "); - print_string( stderr, buf, n ); + print_string( stderr, buf, n, 0 ); putc('\n', stderr); state = fhdrERROR; } @@ -531,7 +531,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, case fhdrERRORShow: log_error("invalid clear text header: "); - print_string( stderr, buf, n ); + print_string( stderr, buf, n, 0 ); putc('\n', stderr); state = fhdrERROR; break; diff --git a/g10/build-packet.c b/g10/build-packet.c index ea8a875ad..793095e65 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -175,13 +175,20 @@ do_public_cert( IOBUF out, int ctb, PKT_public_cert *pkc ) else iobuf_put( a, pkc->version ); write_32(a, pkc->timestamp ); - write_16(a, pkc->valid_days ); + if( pkc->version < 4 ) + write_16(a, pkc->valid_days ); iobuf_put(a, pkc->pubkey_algo ); if( pkc->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) { mpi_write(a, pkc->d.elg.p ); mpi_write(a, pkc->d.elg.g ); mpi_write(a, pkc->d.elg.y ); } + else if( pkc->pubkey_algo == PUBKEY_ALGO_DSA ) { + mpi_write(a, pkc->d.dsa.p ); + mpi_write(a, pkc->d.dsa.q ); + mpi_write(a, pkc->d.dsa.g ); + mpi_write(a, pkc->d.dsa.y ); + } else if( pkc->pubkey_algo == PUBKEY_ALGO_RSA ) { mpi_write(a, pkc->d.rsa.rsa_n ); mpi_write(a, pkc->d.rsa.rsa_e ); @@ -253,7 +260,8 @@ do_secret_cert( IOBUF out, int ctb, PKT_secret_cert *skc ) else iobuf_put( a, skc->version ); write_32(a, skc->timestamp ); - write_16(a, skc->valid_days ); + if( skc->version < 4 ) + write_16(a, skc->valid_days ); iobuf_put(a, skc->pubkey_algo ); if( skc->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) { mpi_write(a, skc->d.elg.p ); @@ -416,24 +424,35 @@ do_signature( IOBUF out, int ctb, PKT_signature *sig ) int rc = 0; IOBUF a = iobuf_temp(); - write_version( a, ctb ); - iobuf_put(a, 5 ); /* constant */ + if( !sig->version ) + iobuf_put( a, 3 ); + else + iobuf_put( a, sig->version ); + if( sig->version < 4 ) + iobuf_put(a, 5 ); /* constant */ iobuf_put(a, sig->sig_class ); - write_32(a, sig->timestamp ); - write_32(a, sig->keyid[0] ); - write_32(a, sig->keyid[1] ); + if( sig->version < 4 ) { + write_32(a, sig->timestamp ); + write_32(a, sig->keyid[0] ); + write_32(a, sig->keyid[1] ); + } iobuf_put(a, sig->pubkey_algo ); + iobuf_put(a, sig->digest_algo ); + if( sig->version >= 4 ) { + /* fixme: write v4 subpackets here */ + log_error("WARNING: note writing of v4 subpackets is not implemented\n"); + } + iobuf_put(a, sig->digest_start[0] ); + iobuf_put(a, sig->digest_start[1] ); if( sig->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) { - iobuf_put(a, sig->d.elg.digest_algo ); - iobuf_put(a, sig->d.elg.digest_start[0] ); - iobuf_put(a, sig->d.elg.digest_start[1] ); mpi_write(a, sig->d.elg.a ); mpi_write(a, sig->d.elg.b ); } + else if( sig->pubkey_algo == PUBKEY_ALGO_DSA ) { + mpi_write(a, sig->d.dsa.r ); + mpi_write(a, sig->d.dsa.s ); + } else if( sig->pubkey_algo == PUBKEY_ALGO_RSA ) { - iobuf_put(a, sig->d.rsa.digest_algo ); - iobuf_put(a, sig->d.rsa.digest_start[0] ); - iobuf_put(a, sig->d.rsa.digest_start[1] ); mpi_write(a, sig->d.rsa.rsa_integer ); } else { diff --git a/g10/compress.c b/g10/compress.c index ebad43045..686332b82 100644 --- a/g10/compress.c +++ b/g10/compress.c @@ -230,9 +230,11 @@ compress_filter( void *opaque, int control, * Handle a compressed packet */ int -handle_compressed( PKT_compressed *cd ) +handle_compressed( PKT_compressed *cd, + int (*callback)(IOBUF, void *), void *passthru ) { compress_filter_context_t cfx; + int rc; memset( &cfx, 0, sizeof cfx ); if( cd->algorithm == 1 ) @@ -241,7 +243,10 @@ handle_compressed( PKT_compressed *cd ) return G10ERR_COMPR_ALGO; iobuf_push_filter( cd->buf, compress_filter, &cfx ); - proc_packets(cd->buf); + if( callback ) + rc = callback(cd->buf, passthru ); + else + rc = proc_packets(cd->buf); iobuf_pop_filter( cd->buf, compress_filter, &cfx ); #if 0 if( cd->len ) @@ -250,6 +255,6 @@ handle_compressed( PKT_compressed *cd ) iobuf_clear_eof( cd->buf ); #endif cd->buf = NULL; - return 0; + return rc; } diff --git a/g10/decrypt.c b/g10/decrypt.c new file mode 100644 index 000000000..1a457f2df --- /dev/null +++ b/g10/decrypt.c @@ -0,0 +1,82 @@ +/* verify.c - verify signed data + * 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 + +#include "options.h" +#include "packet.h" +#include "errors.h" +#include "iobuf.h" +#include "keydb.h" +#include "memory.h" +#include "util.h" +#include "main.h" +#include "i18n.h" + + + +/**************** + * Assume that the input is an encrypted message and decrypt + * (and if signed, verify the signature) it. + * This command differs from the default operation, as it never + * write to the filename which is included in the file and that it + * rejects files which don't begin with an encrypted message. + */ + +int +decrypt_message( const char *filename ) +{ + IOBUF fp; + armor_filter_context_t afx; + int rc; + int no_out=0; + + /* open the message file */ + fp = iobuf_open(filename); + if( !fp ) { + log_error(_("can't open '%s'\n"), print_fname_stdin(filename)); + return G10ERR_OPEN_FILE; + } + + if( !opt.no_armor ) { + if( use_armor_filter( fp ) ) { + memset( &afx, 0, sizeof afx); + iobuf_push_filter( fp, armor_filter, &afx ); + } + } + + if( !opt.outfile ) { + no_out = 1; + opt.outfile = "-"; + } + rc = proc_encryption_packets( fp ); + if( no_out ) + opt.outfile = NULL; + iobuf_close(fp); + return rc; +} + + + diff --git a/g10/dsa.c b/g10/dsa.c new file mode 100644 index 000000000..1c4f8945d --- /dev/null +++ b/g10/dsa.c @@ -0,0 +1,71 @@ +/* dsa.c + * 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 + +#include "options.h" +#include "packet.h" +#include "errors.h" +#include "iobuf.h" +#include "keydb.h" +#include "memory.h" +#include "util.h" +#include "main.h" + + +void +g10_dsa_sign( PKT_secret_cert *skc, PKT_signature *sig, + MD_HANDLE md, int digest_algo ) +{ + DSA_secret_key skey; + MPI frame; + byte *dp; + + assert( sig->pubkey_algo == PUBKEY_ALGO_DSA ); + if( !digest_algo ) + digest_algo = md_get_algo(md); + + dp = md_read( md, digest_algo ); + keyid_from_skc( skc, sig->keyid ); + sig->digest_algo = digest_algo; + sig->digest_start[0] = dp[0]; + sig->digest_start[1] = dp[1]; + sig->d.dsa.r = mpi_alloc( mpi_get_nlimbs(skc->d.dsa.p) ); + sig->d.dsa.s = mpi_alloc( mpi_get_nlimbs(skc->d.dsa.p) ); + frame = encode_md_value( md, mpi_get_nbits(skc->d.dsa.p)); + skey.p = skc->d.elg.p; + skey.g = skc->d.elg.g; + skey.y = skc->d.elg.y; + skey.x = skc->d.elg.x; + dsa_sign( sig->d.dsa.r, sig->d.dsa.s, frame, &skey); + memset( &skey, 0, sizeof skey ); + mpi_free(frame); + if( opt.verbose ) { + char *ustr = get_user_id_string( sig->keyid ); + log_info("DSA signature from: %s\n", ustr ); + m_free(ustr); + } +} + diff --git a/g10/elg.c b/g10/elg.c index 62c5014c8..329b762fe 100644 --- a/g10/elg.c +++ b/g10/elg.c @@ -79,9 +79,9 @@ g10_elg_sign( PKT_secret_cert *skc, PKT_signature *sig, dp = md_read( md, digest_algo ); keyid_from_skc( skc, sig->keyid ); - sig->d.elg.digest_algo = digest_algo; - sig->d.elg.digest_start[0] = dp[0]; - sig->d.elg.digest_start[1] = dp[1]; + sig->digest_algo = digest_algo; + sig->digest_start[0] = dp[0]; + sig->digest_start[1] = dp[1]; sig->d.elg.a = mpi_alloc( mpi_get_nlimbs(skc->d.elg.p) ); sig->d.elg.b = mpi_alloc( mpi_get_nlimbs(skc->d.elg.p) ); frame = encode_md_value( md, mpi_get_nbits(skc->d.elg.p)); diff --git a/g10/free-packet.c b/g10/free-packet.c index fa1a53ccf..eb90f51ef 100644 --- a/g10/free-packet.c +++ b/g10/free-packet.c @@ -45,31 +45,41 @@ free_pubkey_enc( PKT_pubkey_enc *enc ) } void -free_seckey_enc( PKT_signature *enc ) +free_seckey_enc( PKT_signature *sig ) { - if( enc->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) { - mpi_free( enc->d.elg.a ); - mpi_free( enc->d.elg.b ); + if( sig->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) { + mpi_free( sig->d.elg.a ); + mpi_free( sig->d.elg.b ); } - else if( enc->pubkey_algo == PUBKEY_ALGO_RSA ) - mpi_free( enc->d.rsa.rsa_integer ); - m_free(enc); + else if( sig->pubkey_algo == PUBKEY_ALGO_DSA ) { + mpi_free( sig->d.dsa.r ); + mpi_free( sig->d.dsa.s ); + } + else if( sig->pubkey_algo == PUBKEY_ALGO_RSA ) + mpi_free( sig->d.rsa.rsa_integer ); + m_free(sig->hashed_data); + m_free(sig->unhashed_data); + m_free(sig); } /**************** - * Return the digest algorith from the signature packet. + * Return the digest algorithm from the signature packet. * We need this function because the digeste algo depends on the * used pubkey algorithm. */ int digest_algo_from_sig( PKT_signature *sig ) { + #if 0 /* not used anymore */ switch( sig->pubkey_algo ) { case PUBKEY_ALGO_ELGAMAL: return sig->d.elg.digest_algo; + case PUBKEY_ALGO_DSA: return sig->d.dsa.digest_algo; case PUBKEY_ALGO_RSA: return sig->d.rsa.digest_algo; default: return 0; } + #endif + return sig->digest_algo; } @@ -83,6 +93,12 @@ release_public_cert_parts( PKT_public_cert *cert ) mpi_free( cert->d.elg.g ); cert->d.elg.g = NULL; mpi_free( cert->d.elg.y ); cert->d.elg.y = NULL; } + else if( cert->pubkey_algo == PUBKEY_ALGO_DSA ) { + mpi_free( cert->d.dsa.p ); cert->d.dsa.p = NULL; + mpi_free( cert->d.dsa.q ); cert->d.dsa.q = NULL; + mpi_free( cert->d.dsa.g ); cert->d.dsa.g = NULL; + mpi_free( cert->d.dsa.y ); cert->d.dsa.y = NULL; + } else if( cert->pubkey_algo == PUBKEY_ALGO_RSA ) { mpi_free( cert->d.rsa.rsa_n ); cert->d.rsa.rsa_n = NULL; mpi_free( cert->d.rsa.rsa_e ); cert->d.rsa.rsa_e = NULL; @@ -107,6 +123,12 @@ copy_public_cert( PKT_public_cert *d, PKT_public_cert *s ) d->d.elg.g = mpi_copy( s->d.elg.g ); d->d.elg.y = mpi_copy( s->d.elg.y ); } + else if( s->pubkey_algo == PUBKEY_ALGO_DSA ) { + d->d.dsa.p = mpi_copy( s->d.dsa.p ); + d->d.dsa.q = mpi_copy( s->d.dsa.q ); + d->d.dsa.g = mpi_copy( s->d.dsa.g ); + d->d.dsa.y = mpi_copy( s->d.dsa.y ); + } else if( s->pubkey_algo == PUBKEY_ALGO_RSA ) { d->d.rsa.rsa_n = mpi_copy( s->d.rsa.rsa_n ); d->d.rsa.rsa_e = mpi_copy( s->d.rsa.rsa_e ); @@ -123,6 +145,13 @@ release_secret_cert_parts( PKT_secret_cert *cert ) mpi_free( cert->d.elg.y ); cert->d.elg.y = NULL; mpi_free( cert->d.elg.x ); cert->d.elg.x = NULL; } + else if( cert->pubkey_algo == PUBKEY_ALGO_DSA ) { + mpi_free( cert->d.dsa.p ); cert->d.dsa.p = NULL; + mpi_free( cert->d.dsa.q ); cert->d.dsa.q = NULL; + mpi_free( cert->d.dsa.g ); cert->d.dsa.g = NULL; + mpi_free( cert->d.dsa.y ); cert->d.dsa.y = NULL; + mpi_free( cert->d.dsa.x ); cert->d.dsa.x = NULL; + } else if( cert->pubkey_algo == PUBKEY_ALGO_RSA ) { mpi_free( cert->d.rsa.rsa_n ); cert->d.rsa.rsa_n = NULL; mpi_free( cert->d.rsa.rsa_e ); cert->d.rsa.rsa_e = NULL; @@ -152,6 +181,13 @@ copy_secret_cert( PKT_secret_cert *d, PKT_secret_cert *s ) d->d.elg.y = mpi_copy( s->d.elg.y ); d->d.elg.x = mpi_copy( s->d.elg.x ); } + else if( s->pubkey_algo == PUBKEY_ALGO_DSA ) { + d->d.dsa.p = mpi_copy( s->d.dsa.p ); + d->d.dsa.q = mpi_copy( s->d.dsa.q ); + d->d.dsa.g = mpi_copy( s->d.dsa.g ); + d->d.dsa.y = mpi_copy( s->d.dsa.y ); + d->d.dsa.x = mpi_copy( s->d.dsa.x ); + } else if( s->pubkey_algo == PUBKEY_ALGO_RSA ) { d->d.rsa.rsa_n = mpi_copy( s->d.rsa.rsa_n ); d->d.rsa.rsa_e = mpi_copy( s->d.rsa.rsa_e ); @@ -290,6 +326,16 @@ cmp_public_certs( PKT_public_cert *a, PKT_public_cert *b ) if( mpi_cmp( a->d.elg.y , b->d.elg.y ) ) return -1; } + else if( a->pubkey_algo == PUBKEY_ALGO_DSA ) { + if( mpi_cmp( a->d.dsa.p , b->d.dsa.p ) ) + return -1; + if( mpi_cmp( a->d.dsa.q , b->d.dsa.q ) ) + return -1; + if( mpi_cmp( a->d.dsa.g , b->d.dsa.g ) ) + return -1; + if( mpi_cmp( a->d.dsa.y , b->d.dsa.y ) ) + return -1; + } else if( a->pubkey_algo == PUBKEY_ALGO_RSA ) { if( mpi_cmp( a->d.rsa.rsa_n , b->d.rsa.rsa_n ) ) return -1; @@ -321,6 +367,16 @@ cmp_public_secret_cert( PKT_public_cert *pkc, PKT_secret_cert *skc ) if( mpi_cmp( pkc->d.elg.y , skc->d.elg.y ) ) return -1; } + else if( pkc->pubkey_algo == PUBKEY_ALGO_DSA ) { + if( mpi_cmp( pkc->d.dsa.p , skc->d.dsa.p ) ) + return -1; + if( mpi_cmp( pkc->d.dsa.q , skc->d.dsa.q ) ) + return -1; + if( mpi_cmp( pkc->d.dsa.g , skc->d.dsa.g ) ) + return -1; + if( mpi_cmp( pkc->d.dsa.y , skc->d.dsa.y ) ) + return -1; + } else if( pkc->pubkey_algo == PUBKEY_ALGO_RSA ) { if( mpi_cmp( pkc->d.rsa.rsa_n , skc->d.rsa.rsa_n ) ) return -1; diff --git a/g10/g10.c b/g10/g10.c index b5ee4e4e3..d2683c95c 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -63,9 +63,13 @@ static ARGPARSE_OPTS opts[] = { { 'c', "symmetric", 0, N_("encryption only with symmetric cipher")}, { 507, "store", 0, N_("store only")}, { 'd', "decrypt", 0, N_("decrypt data (default)")}, - { 'k', "list-keys", 0, N_("list keys")}, - { 508, "check-keys",0, N_("check signatures on a key in the keyring")}, - { 515, "fingerprint", 0, N_("show the fingerprints")}, + { 550, "verify" , 0, N_("verify a signature")}, + #endif + { 551, "list-keys", 0, N_("list keys")}, + { 552, "list-sigs", 0, N_("list keys and signatures")}, + { 508, "check-sigs",0, N_("check key signatures")}, + { 515, "fingerprint", 0, N_("list keys and fingerprints")}, + #ifdef IS_G10 { 503, "gen-key", 0, N_("generate a new key pair")}, { 506, "sign-key" ,0, N_("make a signature on a key in the keyring")}, { 505, "delete-key",0, N_("remove key from the public keyring")}, @@ -82,7 +86,6 @@ static ARGPARSE_OPTS opts[] = { { 516, "print-mds" , 0, N_("print all message digests")}, { 513, "gen-prime" , 0, "\r" }, { 548, "gen-random" , 0, "\r" }, - { 549, "ext-list-keys", 0, "Print a parsable list of keys" }, #endif { 301, NULL, 0, N_("\v\nOptions:\n ") }, @@ -132,6 +135,7 @@ static ARGPARSE_OPTS opts[] = { { 533, "list-trust-path",0, "\r"}, #endif #ifdef IS_G10 + { 'k', NULL, 0, "\r"}, { 504, "delete-secret-key",0, "\r" }, { 524, "edit-sig" ,0, "\r"}, /* alias for edit-key */ { 523, "passphrase-fd",1, "\r" }, @@ -147,6 +151,10 @@ static ARGPARSE_OPTS opts[] = { { 543, "no-options", 0, "\r" }, /* shortcut for --options /dev/null */ { 544, "homedir", 2, "\r" }, /* defaults to "~/.gnupg" */ { 545, "no-batch", 0, "\r" }, + { 549, "with-colons", 0, "\r"}, + { 551, "list-key", 0, "\r" }, /* alias */ + { 552, "list-sig", 0, "\r" }, /* alias */ + { 508, "check-sig",0, "\r" }, /* alias */ {0} }; @@ -157,8 +165,9 @@ static ARGPARSE_OPTS opts[] = { enum cmd_values { aNull = 0, aSym, aStore, aEncr, aKeygen, aSign, aSignEncr, aSignKey, aClearsign, aListPackets, aEditSig, aDeleteKey, aDeleteSecretKey, - aKMode, aKModeC, aChangePass, aImport, - aExport, aCheckKeys, aGenRevoke, aPrimegen, aPrintMDs, aExtKeyList, + aKMode, aKModeC, aChangePass, aImport, aVerify, aDecrypt, aListKeys, + aListSigs, + aExport, aCheckKeys, aGenRevoke, aPrimegen, aPrintMDs, aListTrustDB, aListTrustPath, aDeArmor, aEnArmor, aGenRandom, aTest, aNOP }; @@ -358,7 +367,7 @@ main( int argc, char **argv ) int rc=0; int orig_argc; char **orig_argv; - const char *fname, *fname_print; + const char *fname; STRLIST sl, remusr= NULL, locusr=NULL; int nrings=0, sec_nrings=0; armor_filter_context_t afx; @@ -458,11 +467,10 @@ main( int argc, char **argv ) #ifdef IS_G10 case 'a': opt.armor = 1; opt.no_armor=0; break; - case 'b': detached_sig = 1; /* fall trough */ - case 'c': set_cmd( &cmd , aSym); break; - case 'd': break; /* it is default */ + case 'b': detached_sig = 1; set_cmd( &cmd, aSign ); break; + case 'c': set_cmd( &cmd, aSym); break; + case 'd': set_cmd( &cmd, aDecrypt); break; case 'e': set_cmd( &cmd, aEncr); break; - case 'k': set_cmd( &cmd, aKMode ); break; case 'r': /* store the remote users */ sl = m_alloc( sizeof *sl + strlen(pargs.r.ret_str)); strcpy(sl->d, pargs.r.ret_str); @@ -483,9 +491,6 @@ main( int argc, char **argv ) case 505: set_cmd( &cmd, aDeleteKey); break; case 506: set_cmd( &cmd, aSignKey); break; case 507: set_cmd( &cmd, aStore); break; - case 508: set_cmd( &cmd, aCheckKeys); - opt.check_sigs = 1; opt.list_sigs = 1; break; - case 515: opt.fingerprint = 1; break; case 523: set_passphrase_fd( pargs.r.ret_int ); break; case 524: set_cmd( &cmd, aEditSig); break; case 525: set_cmd( &cmd, aChangePass); break; @@ -501,6 +506,7 @@ main( int argc, char **argv ) case 539: set_cmd( &cmd, aClearsign); break; case 540: secmem_set_flags( secmem_get_flags() | 1 ); break; case 542: set_cmd( &cmd, aGenRevoke); break; + case 550: set_cmd( &cmd, aVerify); break; #endif /* IS_G10 */ #ifdef IS_G10MAINT @@ -513,19 +519,21 @@ main( int argc, char **argv ) case 546: set_cmd( &cmd, aDeArmor); break; case 547: set_cmd( &cmd, aEnArmor); break; case 548: set_cmd( &cmd, aGenRandom); break; - case 549: set_cmd( &cmd, aExtKeyList); break; #endif /* IS_G10MAINT */ case 'o': opt.outfile = pargs.r.ret_str; break; case 'v': opt.verbose++; opt.list_sigs=1; break; + case 'k': set_cmd( &cmd, aKMode ); break; case 500: opt.batch = 1; greeting = 0; break; case 501: opt.answer_yes = 1; break; case 502: opt.answer_no = 1; break; + case 508: set_cmd( &cmd, aCheckKeys); break; case 509: add_keyring(pargs.r.ret_str); nrings++; break; case 510: opt.debug |= pargs.r.ret_ulong; break; case 511: opt.debug = ~0; break; case 512: set_status_fd( pargs.r.ret_int ); break; + case 515: opt.fingerprint = 1; break; case 517: add_secret_keyring(pargs.r.ret_str); sec_nrings++; break; case 518: /* config files may not be nested (silently ignore them) */ @@ -551,6 +559,9 @@ main( int argc, char **argv ) case 543: break; /* no-options */ case 544: opt.homedir = pargs.r.ret_str; break; case 545: opt.batch = 0; break; + case 549: opt.with_colons=':'; break; + case 551: set_cmd( &cmd, aListKeys); break; + case 552: set_cmd( &cmd, aListSigs); break; default : errors++; pargs.err = configfp? 1:2; break; } } @@ -576,9 +587,12 @@ main( int argc, char **argv ) /* Okay, we are now working under our real uid */ #endif - write_status( STATUS_ENTER ); + /*write_status( STATUS_ENTER );*/ set_debug(); + 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; @@ -593,6 +607,7 @@ main( int argc, char **argv ) opt.verbose = opt.verbose > 1; } + /* kludge to let -sat generate a clear text signature */ if( opt.textmode && !detached_sig && opt.armor && cmd == aSign ) cmd = aClearsign; @@ -600,23 +615,24 @@ main( int argc, char **argv ) if( opt.verbose > 1 ) set_packet_list_mode(1); - if( cmd != aDeArmor && cmd != aEnArmor ) { + /* 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_secret_keyring("secring.gpg"); if( !nrings || default_keyring ) /* add default ring */ add_keyring("pubring.gpg"); } - if( argc ) { - fname_print = fname = *argv; - } + if( argc ) + fname = *argv; else { - fname_print = "[stdin]"; fname = NULL; if( get_passphrase_fd() == 0 ) { /* reading data and passphrase form stdin: * we assume the first line is the passphrase, so - * we read it now + * we better should read it now. */ /* FIXME: doit */ } @@ -629,6 +645,10 @@ main( int argc, char **argv ) case aDeArmor: case aEnArmor: break; + case aKMode: + case aListKeys: + case aCheckKeys: + break; case aListTrustDB: rc = init_trustdb( argc? 1:0, trustdb_name ); break; default: rc = init_trustdb(1, trustdb_name ); break; } @@ -642,21 +662,22 @@ main( int argc, char **argv ) wrong_args(_("--store [filename]")); if( (rc = encode_store(fname)) ) log_error("%s: store failed: %s\n", - fname_print, g10_errstr(rc) ); + print_fname_stdin(fname), g10_errstr(rc) ); break; #ifdef IS_G10 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("%s: symmetric encryption failed: %s\n", fname_print, g10_errstr(rc) ); + log_error("%s: symmetric encryption failed: %s\n", + print_fname_stdin(fname), 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", fname_print, g10_errstr(rc) ); + log_error("%s: encryption failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) ); break; case aSign: /* sign the given file */ @@ -688,7 +709,7 @@ main( int argc, char **argv ) else sl = NULL; if( (rc = sign_file(sl, detached_sig, locusr, 1, remusr, NULL)) ) - log_error("%s: sign+encrypt failed: %s\n", fname_print, g10_errstr(rc) ); + log_error("%s: sign+encrypt failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) ); free_strlist(sl); break; @@ -696,7 +717,19 @@ main( int argc, char **argv ) if( argc > 1 ) wrong_args(_("--clearsign [filename]")); if( (rc = clearsign_file(fname, locusr, NULL)) ) - log_error("%s: clearsign failed: %s\n", fname_print, g10_errstr(rc) ); + 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; @@ -705,7 +738,7 @@ main( int argc, char **argv ) wrong_args(_("--sign-key username")); /* note: fname is the user id! */ if( (rc = sign_key(fname, locusr)) ) - log_error("%s: sign key failed: %s\n", fname_print, g10_errstr(rc) ); + log_error("%s: sign key failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) ); break; case aEditSig: /* Edit a key signature */ @@ -713,7 +746,7 @@ main( int argc, char **argv ) wrong_args(_("--edit-sig username")); /* note: fname is the user id! */ if( (rc = edit_keysigs(fname)) ) - log_error("%s: edit signature failed: %s\n", fname_print, g10_errstr(rc) ); + log_error("%s: edit signature failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) ); break; case aDeleteSecretKey: @@ -724,7 +757,7 @@ main( int argc, char **argv ) wrong_args(_("--delete-key username")); /* note: fname is the user id! */ if( (rc = delete_key(fname, cmd==aDeleteSecretKey)) ) - log_error("%s: delete key failed: %s\n", fname_print, g10_errstr(rc) ); + log_error("%s: delete key failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) ); break; case aChangePass: /* Change the passphrase */ @@ -732,51 +765,36 @@ main( int argc, char **argv ) wrong_args(_("--change-passphrase [username]")); /* note: fname is the user id! */ if( (rc = change_passphrase(fname)) ) - log_error("%s: change passphrase failed: %s\n", fname_print, + log_error("%s: change passphrase failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) ); break; #endif /* IS_G10 */ case aCheckKeys: + opt.check_sigs = 1; + case aListSigs: + opt.list_sigs = 1; + case aListKeys: + std_key_list( argc, argv ); + break; + case aKMode: /* list keyring */ - if( !argc ) { /* list the default public keyrings */ - int i, seq=0; - const char *s; - - while( (s=get_keyring(seq++)) ) { - if( !(a = iobuf_open(s)) ) { - log_error(_("can't open '%s'\n"), s); - continue; - } - if( seq > 1 ) - putchar('\n'); - printf("%s\n", s ); - for(i=strlen(s); i; i-- ) - putchar('-'); - putchar('\n'); - - proc_packets( a ); - iobuf_close(a); + if( argc < 2 ) /* -kv [userid] */ + std_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 if( cmd == aCheckKeys ) { - log_error("will be soon: --check-keys user-ids\n"); - } - else if( argc == 1) { /* list the given keyring */ - if( !(a = iobuf_open(fname)) ) - log_error(_("can't open '%s'\n"), fname_print); else { - if( !opt.no_armor ) { - memset( &afx, 0, sizeof afx); - iobuf_push_filter( a, armor_filter, &afx ); - } - proc_packets( a ); - iobuf_close(a); + /* add keyring (default keyrings are not registered in this + * special case */ + add_keyring( argv[1] ); + std_key_list( **argv?1:0, argv ); } } else - wrong_args(_("-k[v][v][v][c] [keyring]") ); + wrong_args(_("-k[v][v][v][c] [userid] [keyring]") ); break; #ifdef IS_G10 @@ -901,13 +919,6 @@ main( int argc, char **argv ) list_trust_path( atoi(*argv), argv[1] ); break; - case aExtKeyList: - sl = NULL; - for( ; argc; argc--, argv++ ) - add_to_strlist( &sl, *argv ); - ext_key_list( sl ); - free_strlist(sl); - break; #endif /* IS_G10MAINT */ @@ -921,7 +932,7 @@ main( int argc, char **argv ) if( argc > 1 ) wrong_args(_("[filename]")); if( !(a = iobuf_open(fname)) ) - log_error(_("can't open '%s'\n"), fname_print); + log_error(_("can't open '%s'\n"), print_fname_stdin(fname)); else { if( !opt.no_armor ) { if( use_armor_filter( a ) ) { @@ -953,7 +964,7 @@ g10_exit( int rc ) secmem_dump_stats(); secmem_term(); rc = rc? rc : log_get_errorcount(0)? 2:0; - write_status( STATUS_LEAVE ); + /*write_status( STATUS_LEAVE );*/ exit(rc ); } diff --git a/g10/getkey.c b/g10/getkey.c index b528ed782..300e33b86 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -168,6 +168,7 @@ cache_public_cert( PKT_public_cert *pkc ) u32 keyid[2]; if( pkc->pubkey_algo == PUBKEY_ALGO_ELGAMAL + || pkc->pubkey_algo == PUBKEY_ALGO_DSA || pkc->pubkey_algo == PUBKEY_ALGO_RSA ) { keyid_from_pkc( pkc, keyid ); } @@ -545,6 +546,7 @@ scan_keyring( PKT_public_cert *pkc, u32 *keyid, else if( keyid && pkt.pkttype == PKT_PUBLIC_CERT ) { switch( pkt.pkt.public_cert->pubkey_algo ) { case PUBKEY_ALGO_ELGAMAL: + case PUBKEY_ALGO_DSA: case PUBKEY_ALGO_RSA: keyid_from_pkc( pkt.pkt.public_cert, akeyid ); if( (shortkeyid || akeyid[0] == keyid[0]) @@ -596,6 +598,7 @@ scan_keyring( PKT_public_cert *pkc, u32 *keyid, pkt.pkt.user_id->len, pkt.pkt.user_id->name); else { if( last_pk->pubkey_algo == PUBKEY_ALGO_ELGAMAL + || last_pk->pubkey_algo == PUBKEY_ALGO_DSA || last_pk->pubkey_algo == PUBKEY_ALGO_RSA ) { keyid_from_pkc( last_pk, akeyid ); cache_user_id( pkt.pkt.user_id, akeyid ); @@ -657,6 +660,7 @@ scan_secret_keyring( PKT_secret_cert *skc, u32 *keyid, else if( keyid && pkt.pkttype == PKT_SECRET_CERT ) { switch( pkt.pkt.secret_cert->pubkey_algo ) { case PUBKEY_ALGO_ELGAMAL: + case PUBKEY_ALGO_DSA: case PUBKEY_ALGO_RSA: if( get_first ) { copy_secret_cert( skc, pkt.pkt.secret_cert ); @@ -712,6 +716,7 @@ scan_secret_keyring( PKT_secret_cert *skc, u32 *keyid, pkt.pkt.user_id->len, pkt.pkt.user_id->name); else { if( last_pk->pubkey_algo == PUBKEY_ALGO_ELGAMAL + || last_pk->pubkey_algo == PUBKEY_ALGO_DSA || last_pk->pubkey_algo == PUBKEY_ALGO_RSA ) { keyid_from_skc( last_pk, akeyid ); cache_user_id( pkt.pkt.user_id, akeyid ); diff --git a/g10/import.c b/g10/import.c index a99b4793b..3d5cd2a17 100644 --- a/g10/import.c +++ b/g10/import.c @@ -259,7 +259,7 @@ import_one( const char *fname, KBNODE keyblock ) (ulong)keyid[1], datestr_from_pkc(pkc) ); if( uidnode ) print_string( stderr, uidnode->pkt->pkt.user_id->name, - uidnode->pkt->pkt.user_id->len ); + uidnode->pkt->pkt.user_id->len, 0 ); putc('\n', stderr); } if( !uidnode ) { @@ -532,7 +532,7 @@ delete_inv_parts( const char *fname, KBNODE keyblock, u32 *keyid ) log_info("%s: key %08lX, removed userid '", fname, (ulong)keyid[1]); print_string( stderr, node->pkt->pkt.user_id->name, - node->pkt->pkt.user_id->len ); + node->pkt->pkt.user_id->len, 0 ); fputs("'\n", stderr ); } delete_kbnode( node ); /* the user-id */ diff --git a/g10/kbnode.c b/g10/kbnode.c index 5a6d16abc..11b0e46be 100644 --- a/g10/kbnode.c +++ b/g10/kbnode.c @@ -48,8 +48,8 @@ clone_kbnode( KBNODE node ) KBNODE n = m_alloc( sizeof *n ); n->next = NULL; n->pkt = node->pkt; - n->private_flag |= 2; /* mark cloned */ n->flag = 0; + n->private_flag = node->private_flag | 2; /* mark cloned */ return n; } @@ -268,7 +268,7 @@ dump_kbnode( KBNODE node ) if( node->pkt->pkttype == PKT_USER_ID ) { fputs(" \"", stderr); print_string( stderr, node->pkt->pkt.user_id->name, - node->pkt->pkt.user_id->len ); + node->pkt->pkt.user_id->len, 0 ); fputs("\"\n", stderr); } else if( node->pkt->pkttype == PKT_SIGNATURE ) { diff --git a/g10/keyid.c b/g10/keyid.c index 8d3e58da2..ef7b1ba1b 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -105,6 +105,60 @@ v3_elg_fingerprint_md( PKT_public_cert *pkc ) return md; } +static MD_HANDLE +dsa_fingerprint_md( PKT_public_cert *pkc ) +{ + MD_HANDLE md; + byte *buf1, *buf2, *buf3, *buf4 ; + byte *p1, *p2, *p3, *p4; + unsigned n1, n2, n3, n4; + unsigned nb1, nb2, nb3, nb4; + unsigned n; + + nb1 = mpi_get_nbits(pkc->d.dsa.p); + p1 = buf1 = mpi_get_buffer( pkc->d.dsa.p, &n1, NULL ); + for( ; !*p1 && n1; p1++, n1-- ) /* skip leading null bytes */ + ; + nb2 = mpi_get_nbits(pkc->d.dsa.q); + p2 = buf2 = mpi_get_buffer( pkc->d.dsa.q, &n2, NULL ); + for( ; !*p2 && n2; p2++, n2-- ) + ; + nb3 = mpi_get_nbits(pkc->d.dsa.g); + p3 = buf3 = mpi_get_buffer( pkc->d.dsa.g, &n3, NULL ); + for( ; !*p3 && n3; p3++, n3-- ) + ; + nb4 = mpi_get_nbits(pkc->d.dsa.y); + p4 = buf4 = mpi_get_buffer( pkc->d.dsa.y, &n4, NULL ); + for( ; !*p4 && n4; p4++, n4-- ) + ; + + /* calculate length of packet */ + n = 14 + n1 + n2 + n3 +n4 ; + md = md_open( DIGEST_ALGO_SHA1, 0); + + md_putc( md, 0x99 ); /* ctb */ + md_putc( md, n >> 8 ); /* 2 byte length header */ + md_putc( md, n ); + md_putc( md, 4 ); /* version */ + { u32 a = pkc->timestamp; + md_putc( md, a >> 24 ); + md_putc( md, a >> 16 ); + md_putc( md, a >> 8 ); + md_putc( md, a ); + } + md_putc( md, pkc->pubkey_algo ); + md_putc( md, nb1>>8); md_putc( md, nb1 ); md_write( md, p1, n1 ); + md_putc( md, nb2>>8); md_putc( md, nb2 ); md_write( md, p2, n2 ); + md_putc( md, nb3>>8); md_putc( md, nb3 ); md_write( md, p3, n3 ); + md_putc( md, nb4>>8); md_putc( md, nb4 ); md_write( md, p4, n4 ); + m_free(buf1); + m_free(buf2); + m_free(buf3); + m_free(buf4); + md_final( md ); + + return md; +} static MD_HANDLE v3_elg_fingerprint_md_skc( PKT_secret_cert *skc ) @@ -121,6 +175,21 @@ v3_elg_fingerprint_md_skc( PKT_secret_cert *skc ) return v3_elg_fingerprint_md( &pkc ); } +static MD_HANDLE +dsa_fingerprint_md_skc( PKT_secret_cert *skc ) +{ + PKT_public_cert pkc; + + pkc.pubkey_algo = skc->pubkey_algo; + pkc.timestamp = skc->timestamp; + pkc.pubkey_algo = skc->pubkey_algo; + pkc.d.dsa.p = skc->d.dsa.p; + pkc.d.dsa.q = skc->d.dsa.q; + pkc.d.dsa.g = skc->d.dsa.g; + pkc.d.dsa.y = skc->d.dsa.y; + return dsa_fingerprint_md( &pkc ); +} + /**************** * Get the keyid from the secret key certificate and put it into keyid @@ -145,6 +214,16 @@ keyid_from_skc( PKT_secret_cert *skc, u32 *keyid ) lowbits = keyid[1]; md_close(md); } + else if( skc->pubkey_algo == PUBKEY_ALGO_DSA ) { + const byte *dp; + MD_HANDLE md; + md = dsa_fingerprint_md_skc(skc); + dp = md_read( md, DIGEST_ALGO_SHA1 ); + keyid[0] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ; + keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ; + lowbits = keyid[1]; + md_close(md); + } else if( skc->pubkey_algo == PUBKEY_ALGO_RSA ) { lowbits = mpi_get_keyid( skc->d.rsa.rsa_n, keyid ); } @@ -178,6 +257,16 @@ keyid_from_pkc( PKT_public_cert *pkc, u32 *keyid ) lowbits = keyid[1]; md_close(md); } + else if( pkc->pubkey_algo == PUBKEY_ALGO_DSA ) { + const byte *dp; + MD_HANDLE md; + md = dsa_fingerprint_md(pkc); + dp = md_read( md, DIGEST_ALGO_SHA1 ); + keyid[0] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ; + keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ; + lowbits = keyid[1]; + md_close(md); + } else if( pkc->pubkey_algo == PUBKEY_ALGO_RSA ) { lowbits = mpi_get_keyid( pkc->d.rsa.rsa_n, keyid ); } @@ -208,6 +297,9 @@ nbits_from_pkc( PKT_public_cert *pkc ) if( pkc->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) { return mpi_get_nbits( pkc->d.elg.p ); } + else if( pkc->pubkey_algo == PUBKEY_ALGO_DSA ) { + return mpi_get_nbits( pkc->d.dsa.p ); + } else if( pkc->pubkey_algo == PUBKEY_ALGO_RSA ) { return mpi_get_nbits( pkc->d.rsa.rsa_n ); } @@ -224,6 +316,9 @@ nbits_from_skc( PKT_secret_cert *skc ) if( skc->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) { return mpi_get_nbits( skc->d.elg.p ); } + else if( skc->pubkey_algo == PUBKEY_ALGO_DSA ) { + return mpi_get_nbits( skc->d.dsa.p ); + } else if( skc->pubkey_algo == PUBKEY_ALGO_RSA ) { return mpi_get_nbits( skc->d.rsa.rsa_n ); } @@ -293,6 +388,15 @@ fingerprint_from_skc( PKT_secret_cert *skc, size_t *ret_len ) pkc.d.elg.g = skc->d.elg.g; pkc.d.elg.y = skc->d.elg.y; } + else if( pkc.pubkey_algo == PUBKEY_ALGO_DSA ) { + pkc.timestamp = skc->timestamp; + pkc.valid_days = skc->valid_days; + pkc.pubkey_algo = skc->pubkey_algo; + pkc.d.dsa.p = skc->d.dsa.p; + pkc.d.dsa.q = skc->d.dsa.q; + pkc.d.dsa.g = skc->d.dsa.g; + pkc.d.dsa.y = skc->d.dsa.y; + } else if( pkc.pubkey_algo == PUBKEY_ALGO_RSA ) { pkc.d.rsa.rsa_n = skc->d.rsa.rsa_n; pkc.d.rsa.rsa_e = skc->d.rsa.rsa_e; @@ -322,6 +426,15 @@ fingerprint_from_pkc( PKT_public_cert *pkc, size_t *ret_len ) memcpy(array, dp, 20 ); md_close(md); } + else if( pkc->pubkey_algo == PUBKEY_ALGO_DSA ) { + MD_HANDLE md; + md = dsa_fingerprint_md(pkc); + dp = md_read( md, DIGEST_ALGO_SHA1 ); + array = m_alloc( 20 ); + len = 20; + memcpy(array, dp, 20 ); + md_close(md); + } else if( pkc->pubkey_algo == PUBKEY_ALGO_RSA ) { MD_HANDLE md; diff --git a/g10/keylist.c b/g10/keylist.c index 15f3c2f19..25573663a 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -32,17 +32,231 @@ #include "memory.h" #include "util.h" #include "trustdb.h" -#include "ttyio.h" +#include "main.h" #include "i18n.h" +static void list_all(void); +static void list_one(const char *name); +static void fingerprint( PKT_public_cert *pkc ); + /**************** - * List the keys in a special forma which is extensible and easy to parse - * If NAMES is NULL; the complte keyring is listed - * + * List the keys + * If NNAMES is 0; all available keys are listed */ void -ext_key_list( STRLIST names ) +std_key_list( int nnames, char **names ) { + if( !nnames ) + list_all(); + else { /* List by user id */ + for( ; nnames ; nnames--, names++ ) + list_one( *names ); + } +} + + +static void +list_all() +{ + int i, seq=0; + const char *s; + IOBUF a; + + while( (s=get_keyring(seq++)) ) { + if( !(a = iobuf_open(s)) ) { + log_error(_("can't open %s: %s\n"), s, strerror(errno)); + continue; + } + if( seq > 1 ) + putchar('\n'); + printf("%s\n", s ); + for(i=strlen(s); i; i-- ) + putchar('-'); + putchar('\n'); + + proc_packets( a ); + iobuf_close(a); + } +} + + +static void +list_one( const char *name ) +{ + int rc = 0; + KBNODE keyblock = NULL; + KBNODE kbctx; + KBNODE node; + KBPOS kbpos; + PKT_public_cert *pkc; + u32 keyid[2]; + int any=0; + + /* search the userid */ + rc = find_keyblock_byname( &kbpos, name ); + if( rc ) { + log_error("%s: user not found\n", name ); + goto leave; + } + + /* read the keyblock */ + rc = read_keyblock( &kbpos, &keyblock ); + if( rc ) { + log_error("%s: keyblock read problem: %s\n", name, g10_errstr(rc) ); + goto leave; + } + + /* get the keyid from the keyblock */ + node = find_kbnode( keyblock, PKT_PUBLIC_CERT ); + if( !node ) { + log_error("Oops; public key not found anymore!\n"); + goto leave; + } + + pkc = node->pkt->pkt.public_cert; + keyid_from_pkc( pkc, keyid ); + if( opt.with_colons ) + printf("pub::%u:%d:%08lX%08lX:%s:::", + /* fixme: add trust value here */ + nbits_from_pkc( pkc ), + pkc->pubkey_algo, + (ulong)keyid[0],(ulong)keyid[1], + datestr_from_pkc( pkc ) + /* fixme: add LID and ownertrust here */ + ); + else + printf("pub %4u%c/%08lX %s ", nbits_from_pkc( pkc ), + pubkey_letter( pkc->pubkey_algo ), + (ulong)keyid[1], + datestr_from_pkc( pkc ) ); + + for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) { + if( node->pkt->pkttype == PKT_USER_ID ) { + if( any ) { + if( opt.with_colons ) + printf("uid::::::::"); + else + printf("uid%*s", 28, ""); + } + print_string( stdout, node->pkt->pkt.user_id->name, + node->pkt->pkt.user_id->len, opt.with_colons ); + if( opt.with_colons ) + putchar(':'); + putchar('\n'); + if( !any ) { + if( opt.fingerprint ) + fingerprint( pkc ); + any = 1; + } + } + else if( opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE ) { + PKT_signature *sig = node->pkt->pkt.signature; + int sigrc; + + if( !any ) { /* no user id, (maybe a revocation follows)*/ + if( sig->sig_class == 0x20 ) + puts("[revoked]"); + else + putchar('\n'); + if( opt.fingerprint ) + fingerprint( pkc ); + any=1; + } + + if( sig->sig_class == 0x20 || sig->sig_class == 0x30 ) + fputs("rev", stdout); + else if( (sig->sig_class&~3) == 0x10 ) + fputs("sig", stdout); + else { + if( opt.with_colons ) + printf("sig:::::::::%02x:\n",sig->sig_class ); + else + printf("sig " + "[unexpected signature class 0x%02x]\n",sig->sig_class ); + continue; + } + if( opt.check_sigs ) { + fflush(stdout); + rc = check_key_signature( keyblock, node, NULL ); + switch( rc ) { + case 0: sigrc = '!'; break; + case G10ERR_BAD_SIGN: sigrc = '-'; break; + case G10ERR_NO_PUBKEY: sigrc = '?'; break; + default: sigrc = '%'; break; + } + } + else { + rc = 0; + sigrc = ' '; + } + if( opt.with_colons ) { + putchar(':'); + if( sigrc != ' ' ) + putchar(sigrc); + printf(":::%08lX%08lX:%s:::", (ulong)sig->keyid[0], + (ulong)sig->keyid[1], datestr_from_sig(sig)); + } + else + printf("%c %08lX %s ", + sigrc, (ulong)sig->keyid[1], datestr_from_sig(sig)); + if( sigrc == '%' ) + printf("[%s] ", g10_errstr(rc) ); + else if( sigrc == '?' ) + ; + else { + size_t n; + char *p = get_user_id( sig->keyid, &n ); + print_string( stdout, p, n, opt.with_colons ); + m_free(p); + } + if( opt.with_colons ) + printf(":%02x:", sig->sig_class ); + putchar('\n'); + } + } + if( !any ) {/* oops, no user id */ + if( opt.with_colons ) + putchar(':'); + putchar('\n'); + } + + + leave: + release_kbnode( keyblock ); +} + +static void +fingerprint( PKT_public_cert *pkc ) +{ + byte *array, *p; + size_t i, n; + + p = array = fingerprint_from_pkc( pkc, &n ); + if( opt.with_colons ) { + printf("fpr::::::::"); + for(i=0; i < n ; i++, p++ ) + printf("%02X", *p ); + putchar(':'); + } + else { + printf(" Key fingerprint ="); + if( n == 20 ) { + for(i=0; i < n ; i++, i++, p += 2 ) { + if( i == 10 ) + putchar(' '); + printf(" %02X%02X", *p, p[1] ); + } + } + else { + for(i=0; i < n ; i++, p++ ) { + if( i && !(i%8) ) + putchar(' '); + printf(" %02X", *p ); + } + } + } + putchar('\n'); + m_free(array); } diff --git a/g10/main.h b/g10/main.h index eef3d7377..5078e80aa 100644 --- a/g10/main.h +++ b/g10/main.h @@ -81,6 +81,10 @@ void g10_elg_encrypt( PKT_public_cert *pkc, PKT_pubkey_enc *enc, DEK *dek ); void g10_elg_sign( PKT_secret_cert *skc, PKT_signature *sig, MD_HANDLE md, int digest_algo ); +/*-- dsa.c --*/ +void g10_dsa_sign( PKT_secret_cert *skc, PKT_signature *sig, + MD_HANDLE md, int digest_algo ); + /*-- rsa.c --*/ void g10_rsa_encrypt( PKT_public_cert *pkc, PKT_pubkey_enc *enc, DEK *dek ); void g10_rsa_sign( PKT_secret_cert *skc, PKT_signature *sig, @@ -98,6 +102,15 @@ int enarmor_file( const char *fname ); int gen_revoke( const char *uname ); /*-- keylist.c --*/ -void ext_key_list( STRLIST names ); +void std_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, STRLIST files, int textmode ); #endif /*G10_MAIN_H*/ diff --git a/g10/mainproc.c b/g10/mainproc.c index 153496f60..3ca23ccc0 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "packet.h" @@ -45,6 +46,9 @@ typedef struct { PKT_secret_cert *last_seckey; PKT_user_id *last_user_id; md_filter_context_t mfx; + int sigs_only; /* process only signatures and reject all other stuff */ + int encrypt_only; /* process onyl encrytion messages */ + STRLIST signed_data; DEK *dek; int last_was_pubkey_enc; KBNODE list; /* the current list of packets */ @@ -53,6 +57,7 @@ typedef struct { } *CTX; +static int do_proc_packets( CTX c, IOBUF a ); static void list_node( CTX c, KBNODE node ); static void proc_tree( CTX c, KBNODE node ); @@ -155,6 +160,7 @@ proc_pubkey_enc( CTX c, PACKET *pkt ) enc = pkt->pkt.pubkey_enc; /*printf("enc: encrypted by a pubkey with keyid %08lX\n", enc->keyid[1] );*/ if( enc->pubkey_algo == PUBKEY_ALGO_ELGAMAL + || enc->pubkey_algo == PUBKEY_ALGO_DSA || enc->pubkey_algo == PUBKEY_ALGO_RSA ) { m_free(c->dek ); /* paranoid: delete a pending DEK */ c->dek = m_alloc_secure( sizeof *c->dek ); @@ -234,6 +240,18 @@ proc_plaintext( CTX c, PACKET *pkt ) } +static int +proc_compressed_cb( IOBUF a, void *info ) +{ + return proc_signature_packets( a, ((CTX)info)->signed_data ); +} + +static int +proc_encrypt_cb( IOBUF a, void *info ) +{ + return proc_encryption_packets( a ); +} + static void proc_compressed( CTX c, PACKET *pkt ) { @@ -241,7 +259,12 @@ proc_compressed( CTX c, PACKET *pkt ) int rc; /*printf("zip: compressed data packet\n");*/ - rc = handle_compressed( zd ); + if( c->sigs_only ) + rc = handle_compressed( zd, proc_compressed_cb, c ); + else if( c->encrypt_only ) + rc = handle_compressed( zd, proc_encrypt_cb, c ); + else + rc = handle_compressed( zd, NULL, NULL ); if( rc ) log_error("uncompressing failed: %s\n", g10_errstr(rc)); free_packet(pkt); @@ -337,7 +360,8 @@ print_userid( PACKET *pkt ) printf("ERROR: unexpected packet type %d", pkt->pkttype ); return; } - print_string( stdout, pkt->pkt.user_id->name, pkt->pkt.user_id->len ); + print_string( stdout, pkt->pkt.user_id->name, pkt->pkt.user_id->len, + opt.with_colons ); } @@ -349,19 +373,27 @@ print_fingerprint( PKT_public_cert *pkc, PKT_secret_cert *skc ) p = array = skc? fingerprint_from_skc( skc, &n ) : fingerprint_from_pkc( pkc, &n ); - printf(" Key fingerprint ="); - if( n == 20 ) { - for(i=0; i < n ; i++, i++, p += 2 ) { - if( i == 10 ) - putchar(' '); - printf(" %02X%02X", *p, p[1] ); - } + if( opt.with_colons ) { + printf("fpr::::::::"); + for(i=0; i < n ; i++, p++ ) + printf("%02X", *p ); + putchar(':'); } else { - for(i=0; i < n ; i++, p++ ) { - if( i && !(i%8) ) - putchar(' '); - printf(" %02X", *p ); + printf(" Key fingerprint ="); + if( n == 20 ) { + for(i=0; i < n ; i++, i++, p += 2 ) { + if( i == 10 ) + putchar(' '); + printf(" %02X%02X", *p, p[1] ); + } + } + else { + for(i=0; i < n ; i++, p++ ) { + if( i && !(i%8) ) + putchar(' '); + printf(" %02X", *p ); + } } } putchar('\n'); @@ -383,24 +415,47 @@ list_node( CTX c, KBNODE node ) else if( node->pkt->pkttype == PKT_PUBLIC_CERT ) { PKT_public_cert *pkc = node->pkt->pkt.public_cert; - printf("pub %4u%c/%08lX %s ", nbits_from_pkc( pkc ), + if( opt.with_colons ) { + u32 keyid[2]; + keyid_from_pkc( pkc, keyid ); + printf("pub::%u:%d:%08lX%08lX:%s:::", + /* fixme: add trust value here */ + nbits_from_pkc( pkc ), + pkc->pubkey_algo, + (ulong)keyid[0],(ulong)keyid[1], + datestr_from_pkc( pkc ) + /* fixme: add LID and ownertrust here */ + ); + } + else + printf("pub %4u%c/%08lX %s ", nbits_from_pkc( pkc ), pubkey_letter( pkc->pubkey_algo ), (ulong)keyid_from_pkc( pkc, NULL ), datestr_from_pkc( pkc ) ); /* and now list all userids with their signatures */ for( node = node->next; node; node = node->next ) { if( any != 2 && node->pkt->pkttype == PKT_SIGNATURE ) { - if( !any ) - putchar('\n'); + if( !any ) { + if( node->pkt->pkt.signature->sig_class == 0x20 ) + puts("[revoked]"); + else + putchar('\n'); + } list_node(c, node ); any = 1; } else if( node->pkt->pkttype == PKT_USER_ID ) { KBNODE n; - if( any ) - printf( "%*s", 31, "" ); + if( any ) { + if( opt.with_colons ) + printf("uid::::::::"); + else + printf( "uid%*s", 28, "" ); + } print_userid( node->pkt ); + if( opt.with_colons ) + putchar(':'); putchar('\n'); if( opt.fingerprint && !any ) print_fingerprint( pkc, NULL ); @@ -457,17 +512,27 @@ list_node( CTX c, KBNODE node ) default: sigrc = '%'; break; } } - printf("%c %08lX %s ", - sigrc, (ulong)sig->keyid[1], datestr_from_sig(sig)); + if( opt.with_colons ) { + putchar(':'); + if( sigrc != ' ' ) + putchar(sigrc); + printf(":::%08lX%08lX:%s:::", (ulong)sig->keyid[0], + (ulong)sig->keyid[1], datestr_from_sig(sig)); + } + else + printf("%c %08lX %s ", + sigrc, (ulong)sig->keyid[1], datestr_from_sig(sig)); if( sigrc == '%' ) printf("[%s] ", g10_errstr(rc2) ); else if( sigrc == '?' ) ; else { p = get_user_id( sig->keyid, &n ); - print_string( stdout, p, n ); + print_string( stdout, p, n, opt.with_colons ); m_free(p); } + if( opt.with_colons ) + printf(":%02x:", sig->sig_class ); putchar('\n'); } else @@ -479,8 +544,40 @@ int proc_packets( IOBUF a ) { CTX c = m_alloc_clear( sizeof *c ); - PACKET *pkt = m_alloc( sizeof *pkt ); + int rc = do_proc_packets( c, a ); + m_free( c ); + return rc; +} + +int +proc_signature_packets( IOBUF a, STRLIST signedfiles ) +{ + CTX c = m_alloc_clear( sizeof *c ); int rc; + c->sigs_only = 1; + c->signed_data = signedfiles; + rc = do_proc_packets( c, a ); + m_free( c ); + return rc; +} + +int +proc_encryption_packets( IOBUF a ) +{ + CTX c = m_alloc_clear( sizeof *c ); + int rc; + c->encrypt_only = 1; + rc = do_proc_packets( c, a ); + m_free( c ); + return rc; +} + + +int +do_proc_packets( CTX c, IOBUF a ) +{ + PACKET *pkt = m_alloc( sizeof *pkt ); + int rc=0; int newpkt; c->iobuf = a; @@ -507,6 +604,38 @@ proc_packets( IOBUF a ) default: newpkt = 0; break; } } + else if( c->sigs_only ) { + switch( pkt->pkttype ) { + case PKT_PUBLIC_CERT: + case PKT_SECRET_CERT: + case PKT_USER_ID: + case PKT_PUBKEY_ENC: + case PKT_ENCRYPTED: + rc = G10ERR_UNEXPECTED; + goto leave; + case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break; + case PKT_PLAINTEXT: proc_plaintext( c, pkt ); break; + case PKT_COMPRESSED: proc_compressed( c, pkt ); break; + case PKT_ONEPASS_SIG: newpkt = add_onepass_sig( c, pkt ); break; + default: newpkt = 0; break; + } + } + else if( c->encrypt_only ) { + switch( pkt->pkttype ) { + case PKT_PUBLIC_CERT: + case PKT_SECRET_CERT: + case PKT_USER_ID: + rc = G10ERR_UNEXPECTED; + goto leave; + case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break; + case PKT_PUBKEY_ENC: proc_pubkey_enc( c, pkt ); break; + case PKT_ENCRYPTED: proc_encrypted( c, pkt ); break; + case PKT_PLAINTEXT: proc_plaintext( c, pkt ); break; + case PKT_COMPRESSED: proc_compressed( c, pkt ); break; + case PKT_ONEPASS_SIG: newpkt = add_onepass_sig( c, pkt ); break; + default: newpkt = 0; break; + } + } else { switch( pkt->pkttype ) { case PKT_PUBLIC_CERT: newpkt = add_public_cert( c, pkt ); break; @@ -533,14 +662,15 @@ proc_packets( IOBUF a ) else free_packet(pkt); } + rc = 0; + leave: release_list( c ); m_free(c->dek); free_packet( pkt ); m_free( pkt ); free_md_filter_context( &c->mfx ); - m_free( c ); - return 0; + return rc; } @@ -549,7 +679,7 @@ print_keyid( FILE *fp, u32 *keyid ) { size_t n; char *p = get_user_id( keyid, &n ); - print_string( fp, p, n ); + print_string( fp, p, n, opt.with_colons ); m_free(p); } @@ -562,15 +692,18 @@ check_sig_and_print( CTX c, KBNODE node ) int rc; rc = do_check_sig(c, node ); - if( !rc ) { - write_status( STATUS_GOODSIG ); - log_info("Good signature from "); - print_keyid( stderr, sig->keyid ); - putc('\n', stderr); - } - else if( rc == G10ERR_BAD_SIGN ) { - write_status( STATUS_BADSIG ); - log_error("BAD signature from "); + if( !rc || rc == G10ERR_BAD_SIGN ) { + char *p, *buf; + + p = get_user_id_string( sig->keyid ); + buf = m_alloc( 20 + strlen(p) ); + sprintf(buf, "%lu %s", (ulong)sig->timestamp, p ); + m_free(p); + if( (p=strchr(buf,'\n')) ) + *p = 0; /* just in case ... */ + write_status_text( rc? STATUS_BADSIG : STATUS_GOODSIG, buf ); + m_free(buf); + log_info("%s signature from ", rc? "BAD":"Good"); print_keyid( stderr, sig->keyid ); putc('\n', stderr); if( opt.batch ) @@ -607,12 +740,17 @@ proc_tree( CTX c, KBNODE node ) free_md_filter_context( &c->mfx ); /* prepare to create all requested message digests */ c->mfx.md = md_open(0, 0); + /* fixme: why looking for the signature packet and not 1passpacket*/ for( n1 = node; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )); ) { md_enable( c->mfx.md, digest_algo_from_sig(n1->pkt->pkt.signature)); } /* ask for file and hash it */ - rc = ask_for_detached_datafile( &c->mfx, + if( c->sigs_only ) + rc = hash_datafiles( c->mfx.md, c->signed_data, + n1->pkt->pkt.onepass_sig->sig_class == 0x01 ); + else + rc = ask_for_detached_datafile( &c->mfx, iobuf_get_fname(c->iobuf)); if( rc ) { log_error("can't hash datafile: %s\n", g10_errstr(rc)); @@ -629,7 +767,11 @@ proc_tree( CTX c, KBNODE node ) if( !c->have_data ) { free_md_filter_context( &c->mfx ); c->mfx.md = md_open(digest_algo_from_sig(sig), 0); - rc = ask_for_detached_datafile( &c->mfx, + if( c->sigs_only ) + rc = hash_datafiles( c->mfx.md, c->signed_data, + sig->sig_class == 0x01 ); + else + rc = ask_for_detached_datafile( &c->mfx, iobuf_get_fname(c->iobuf)); if( rc ) { log_error("can't hash datafile: %s\n", g10_errstr(rc)); diff --git a/g10/openfile.c b/g10/openfile.c index 249e9c9a1..f51ee433d 100644 --- a/g10/openfile.c +++ b/g10/openfile.c @@ -42,6 +42,8 @@ int overwrite_filep( const char *fname ) { + if( !fname || (*fname == '-' && !fname[1]) ) + return 0; /* stdout */ if( !access( fname, F_OK ) ) { char *p; int okay; @@ -55,7 +57,6 @@ overwrite_filep( const char *fname ) okay = 0; while( !okay ) { - if( !okay ) if( first ) { tty_printf("File '%s' exists. ", fname); first = 0; @@ -91,7 +92,7 @@ open_outfile( const char *iname, int mode ) IOBUF a = NULL; int rc; - if( !iname && !opt.outfile ) { + if( (!iname || (*iname=='-' && !iname[1])) && !opt.outfile ) { if( !(a = iobuf_create(NULL)) ) log_error("can't open [stdout]: %s\n", strerror(errno) ); else if( opt.verbose ) @@ -133,7 +134,7 @@ open_sigfile( const char *iname ) IOBUF a = NULL; size_t len; - if( iname ) { + if( iname && !(*iname == '-' && !iname[1]) ) { len = strlen(iname); if( len > 4 && ( !strcmp(iname + len - 4, ".sig") || !strcmp(iname + len - 4, ".asc")) ) { diff --git a/g10/options.h b/g10/options.h index 0ac04184b..0f2a2789f 100644 --- a/g10/options.h +++ b/g10/options.h @@ -31,7 +31,7 @@ struct { int answer_yes; /* answer yes on most questions */ int answer_no; /* answer no on most questions */ int check_sigs; /* check key signatures */ - int reserved1; + int with_colons; int fingerprint; /* list fingerprints */ int list_sigs; /* list signatures */ int no_armor; diff --git a/g10/packet.h b/g10/packet.h index 201313668..53448bd8e 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -75,18 +75,22 @@ typedef struct { u32 keyid[2]; /* 64 bit keyid */ ulong local_id; /* internal use, valid if > 0 */ u32 timestamp; /* signature made */ + byte version; byte sig_class; /* sig classification, append for MD calculation*/ byte pubkey_algo; /* algorithm used for public key scheme */ /* (PUBKEY_ALGO_xxx) */ + byte digest_algo; /* algorithm used for digest (DIGEST_ALGO_xxxx) */ + byte *hashed_data; /* all subpackets with hashed data (v4 only) */ + byte *unhashed_data; /* ditto for unhashed data */ + byte digest_start[2]; /* first 2 bytes of the digest */ union { struct { - byte digest_algo; /* algorithm used for digest (DIGEST_ALGO_xxxx) */ - byte digest_start[2]; /* first 2 byte of the digest */ MPI a, b; /* integers with the digest */ } elg; struct { - byte digest_algo; /* algorithm used for digest (DIGEST_ALGO_xxxx) */ - byte digest_start[2]; /* first 2 byte of the digest */ + MPI r, s; /* integers with the digest */ + } dsa; + struct { MPI rsa_integer; /* the encrypted digest */ } rsa; } d; @@ -106,6 +110,12 @@ typedef struct { MPI g; /* group generator */ MPI y; /* g^x mod p */ } elg; + struct { + MPI p; /* prime */ + MPI q; /* group order */ + MPI g; /* group generator */ + MPI y; /* g^x mod p */ + } dsa; struct { MPI rsa_n; /* public modulus */ MPI rsa_e; /* public exponent */ @@ -138,6 +148,25 @@ typedef struct { } protect; /* when protected, the MPIs above are pointers * to plain storage */ } elg; + struct { + MPI p; /* prime */ + MPI q; /* group order */ + MPI g; /* group generator */ + MPI y; /* g^x mod p */ + MPI x; /* secret exponent */ + u16 csum; /* checksum */ + byte is_protected; /* The above infos are protected and must */ + /* be decrypteded before use. */ + struct { + byte algo; /* cipher used to protect the secret informations*/ + byte s2k; + byte hash; + byte salt[8]; + byte count; + byte iv[8]; /* initialization vector for CFB mode */ + } protect; /* when protected, the MPIs above are pointers + * to plain storage */ + } dsa; struct { MPI rsa_n; /* public modulus */ MPI rsa_e; /* public exponent */ @@ -215,6 +244,8 @@ struct packet_struct { /*-- mainproc.c --*/ int proc_packets( IOBUF a ); +int proc_signature_packets( IOBUF a, STRLIST signedfiles ); +int proc_encryption_packets( IOBUF a ); int list_packets( IOBUF a ); /*-- parse-packet.c --*/ @@ -260,7 +291,8 @@ int protect_secret_key( PKT_secret_cert *cert, DEK *dek ); int get_session_key( PKT_pubkey_enc *k, DEK *dek ); /*-- compress.c --*/ -int handle_compressed( PKT_compressed *zd ); +int handle_compressed( PKT_compressed *cd, + int (*callback)(IOBUF, void *), void *passthru ); /*-- encr-data.c --*/ int decrypt_data( PKT_encrypted *ed, DEK *dek ); diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 498dd1985..0cc2ece01 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -96,6 +96,17 @@ read_32(IOBUF inp) return a; } +static unsigned long +buffer_to_u32( const byte *buffer ) +{ + unsigned long a; + a = *buffer << 24; + a |= buffer[1] << 16; + a |= buffer[2] << 8; + a |= buffer[3]; + return a; +} + int set_packet_list_mode( int mode ) { @@ -466,49 +477,210 @@ parse_publickey( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) } +static const byte * +parse_subpkt( const byte *buffer, int reqtype ) +{ + int buflen = (*buffer << 8) | buffer[1]; + int type; + int critical; + size_t n; + + buffer += 2; + for(;;) { + if( !buflen ) + return NULL; /* end of packets; not found */ + n = *buffer++; buflen--; + if( n >= 192 ) { + if( buflen < 2 ) + goto too_short; + n = (( n - 192 ) << 8) + *buffer + 192; + buflen--; + } + if( buflen < n ) + goto too_short; + type = *buffer; + if( type & 0x80 ) { + type &= 0x7f; + critical = 1; + } + else + critical = 0; + if( reqtype < 0 ) { /* list packets */ + printf("\t%ssubpacket %d of length %u (%s)\n", + reqtype == -1 ? "hashed ":"", type, n, + type == 2 ? "signature creation time" + : type == 3 ? "signature expiration time" + : type == 4 ? "exportable" + : type == 5 ? "trust signature" + : type == 6 ? "regular expression" + : type == 7 ? "revocable" + : type == 9 ? "key expiration time" + : type ==10 ? "additional recipient request" + : type ==11 ? "preferred symmetric algorithms" + : type ==12 ? "revocation key" + : type ==16 ? "issuer key ID" + : type ==20 ? "notation data" + : type ==21 ? "preferred hash algorithms" + : type ==22 ? "preferred compression algorithms" + : type ==23 ? "key server preferences" + : type ==24 ? "preferred key server" + : "?"); + } + else if( type == reqtype ) + break; /* found */ + buffer += n; buflen -=n; + } + buffer++; + n--; + if( n > buflen ) + goto too_short; + switch( type ) { + case 2: /* signature creation time */ + if( n < 4 ) + break; + return buffer; + case 16:/* issuer key ID */ + if( n < 8 ) + break; + return buffer; + case 3: /* signature expiration time */ + case 4: /* exportable */ + case 5: /* trust signature */ + case 6: /* regular expression */ + case 7: /* revocable */ + case 9: /* key expiration time */ + case 10:/* additional recipient request */ + case 11:/* preferred symmetric algorithms */ + case 12:/* revocation key */ + case 20:/* notation data */ + case 21:/* preferred hash algorithms */ + case 22:/* preferred compression algorithms */ + case 23:/* key server preferences */ + case 24:/* preferred key server */ + default: BUG(); /* not yet needed */ + } + log_error("subpacket of type %d too short\n", type); + return NULL; + + too_short: + log_error("buffer shorter than subpacket\n"); + return NULL; +} + + static int parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, PKT_signature *sig ) { - int version, md5_len; + int md5_len=0; unsigned n; + int is_v4=0; + int rc=0; if( pktlen < 16 ) { log_error("packet(%d) too short\n", pkttype); goto leave; } - version = iobuf_get_noeof(inp); pktlen--; - if( version != 2 && version != 3 ) { - log_error("packet(%d) with unknown version %d\n", pkttype, version); + sig->version = iobuf_get_noeof(inp); pktlen--; + if( sig->version == 4 ) + is_v4=1; + else if( sig->version != 2 && sig->version != 3 ) { + log_error("packet(%d) with unknown version %d\n", pkttype, sig->version); goto leave; } - md5_len = iobuf_get_noeof(inp); pktlen--; + + if( !is_v4 ) { + md5_len = iobuf_get_noeof(inp); pktlen--; + } sig->sig_class = iobuf_get_noeof(inp); pktlen--; - sig->timestamp = read_32(inp); pktlen -= 4; - sig->keyid[0] = read_32(inp); pktlen -= 4; - sig->keyid[1] = read_32(inp); pktlen -= 4; + if( !is_v4 ) { + sig->timestamp = read_32(inp); pktlen -= 4; + sig->keyid[0] = read_32(inp); pktlen -= 4; + sig->keyid[1] = read_32(inp); pktlen -= 4; + } sig->pubkey_algo = iobuf_get_noeof(inp); pktlen--; - if( list_mode ) - printf(":signature packet: keyid %08lX%08lX\n" - "\tversion %d, created %lu, md5len %d, sigclass %02x\n", - (ulong)sig->keyid[0], (ulong)sig->keyid[1], - version, (ulong)sig->timestamp, md5_len, sig->sig_class ); - if( sig->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) { - if( pktlen < 5 ) { - log_error("packet(%d) too short\n", pkttype); + sig->digest_algo = iobuf_get_noeof(inp); pktlen--; + if( is_v4 ) { /* read subpackets */ + n = read_16(inp); pktlen -= 2; /* length of hashed data */ + if( n > 10000 ) { + log_error("signature packet: hashed data too long\n"); + rc = G10ERR_INVALID_PACKET; goto leave; } - sig->d.elg.digest_algo = iobuf_get_noeof(inp); pktlen--; - sig->d.elg.digest_start[0] = iobuf_get_noeof(inp); pktlen--; - sig->d.elg.digest_start[1] = iobuf_get_noeof(inp); pktlen--; + if( n ) { + sig->hashed_data = m_alloc( n + 2 ); + sig->hashed_data[0] = n << 8; + sig->hashed_data[1] = n; + if( iobuf_read(inp, sig->hashed_data+2, n ) != n ) { + log_error("premature eof while reading hashed signature data\n"); + rc = -1; + goto leave; + } + pktlen -= n; + } + n = read_16(inp); pktlen -= 2; /* length of unhashed data */ + if( n > 10000 ) { + log_error("signature packet: unhashed data too long\n"); + rc = G10ERR_INVALID_PACKET; + goto leave; + } + if( n ) { + sig->unhashed_data = m_alloc( n + 2 ); + sig->unhashed_data[0] = n << 8; + sig->unhashed_data[1] = n; + if( iobuf_read(inp, sig->unhashed_data+2, n ) != n ) { + log_error("premature eof while reading unhashed signature data\n"); + rc = -1; + goto leave; + } + pktlen -= n; + } + } + + if( pktlen < 5 ) { /* sanity check */ + log_error("packet(%d) too short\n", pkttype); + rc = G10ERR_INVALID_PACKET; + goto leave; + } + + sig->digest_start[0] = iobuf_get_noeof(inp); pktlen--; + sig->digest_start[1] = iobuf_get_noeof(inp); pktlen--; + + if( is_v4 ) { /*extract required informations */ + const byte *p; + p = parse_subpkt( sig->hashed_data, 2 ); + if( !p ) + log_error("signature packet without timestamp\n"); + else + sig->timestamp = buffer_to_u32(p); + p = parse_subpkt( sig->unhashed_data, 16 ); + if( !p ) + log_error("signature packet without keyid\n"); + else { + sig->keyid[0] = buffer_to_u32(p); + sig->keyid[1] = buffer_to_u32(p+4); + } + } + + if( list_mode ) { + printf(":signature packet: keyid %08lX%08lX\n" + "\tversion %d, created %lu, md5len %d, sigclass %02x\n" + "\tdigest algo %d, begin of digest %02x %02x\n", + (ulong)sig->keyid[0], (ulong)sig->keyid[1], + sig->version, (ulong)sig->timestamp, md5_len, sig->sig_class, + sig->digest_algo, + sig->digest_start[0], sig->digest_start[1] ); + if( is_v4 ) { + parse_subpkt( sig->hashed_data, -1 ); + parse_subpkt( sig->unhashed_data, -2 ); + } + } + if( sig->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) { n = pktlen; sig->d.elg.a = mpi_read(inp, &n, 0 ); pktlen -=n; n = pktlen; sig->d.elg.b = mpi_read(inp, &n, 0 ); pktlen -=n; if( list_mode ) { - printf("\tdigest algo %d, begin of digest %02x %02x\n", - sig->d.elg.digest_algo, - sig->d.elg.digest_start[0], sig->d.elg.digest_start[1] ); printf("\telg a: "); mpi_print(stdout, sig->d.elg.a, mpi_print_mode ); printf("\n\telg b: "); @@ -516,20 +688,23 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, putchar('\n'); } } - else if( sig->pubkey_algo == PUBKEY_ALGO_RSA ) { - if( pktlen < 5 ) { - log_error("packet(%d) too short\n", pkttype); - goto leave; + else if( sig->pubkey_algo == PUBKEY_ALGO_DSA ) { + n = pktlen; + sig->d.dsa.r = mpi_read(inp, &n, 0 ); pktlen -=n; + n = pktlen; + sig->d.dsa.s = mpi_read(inp, &n, 0 ); pktlen -=n; + if( list_mode ) { + printf("\tdsa r: "); + mpi_print(stdout, sig->d.elg.a, mpi_print_mode ); + printf("\n\tdsa s: "); + mpi_print(stdout, sig->d.elg.b, mpi_print_mode ); + putchar('\n'); } - sig->d.rsa.digest_algo = iobuf_get_noeof(inp); pktlen--; - sig->d.rsa.digest_start[0] = iobuf_get_noeof(inp); pktlen--; - sig->d.rsa.digest_start[1] = iobuf_get_noeof(inp); pktlen--; + } + else if( sig->pubkey_algo == PUBKEY_ALGO_RSA ) { n = pktlen; sig->d.rsa.rsa_integer = mpi_read(inp, &n, 0 ); pktlen -=n; if( list_mode ) { - printf("\tdigest algo %d, begin of digest %02x %02x\n", - sig->d.rsa.digest_algo, - sig->d.rsa.digest_start[0], sig->d.rsa.digest_start[1] ); printf("\trsa integer: "); mpi_print(stdout, sig->d.rsa.rsa_integer, mpi_print_mode ); putchar('\n'); @@ -541,7 +716,7 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, leave: skip_rest(inp, pktlen); - return 0; + return rc; } @@ -761,6 +936,137 @@ parse_certificate( IOBUF inp, int pkttype, unsigned long pktlen, log_mpidump("elg x=", cert->d.elg.x ); */ } } + else if( algorithm == PUBKEY_ALGO_DSA ) { + MPI dsa_p, dsa_q, dsa_g, dsa_y; + n = pktlen; dsa_p = mpi_read(inp, &n, 0 ); pktlen -=n; + n = pktlen; dsa_q = mpi_read(inp, &n, 0 ); pktlen -=n; + n = pktlen; dsa_g = mpi_read(inp, &n, 0 ); pktlen -=n; + n = pktlen; dsa_y = mpi_read(inp, &n, 0 ); pktlen -=n; + if( list_mode ) { + printf( "\tdsa p: "); + mpi_print(stdout, dsa_p, mpi_print_mode ); + printf("\n\tdsa q: "); + mpi_print(stdout, dsa_q, mpi_print_mode ); + printf("\n\tdsa g: "); + mpi_print(stdout, dsa_g, mpi_print_mode ); + printf("\n\tdsa y: "); + mpi_print(stdout, dsa_y, mpi_print_mode ); + putchar('\n'); + } + if( pkttype == PKT_PUBLIC_CERT ) { + pkt->pkt.public_cert->d.dsa.p = dsa_p; + pkt->pkt.public_cert->d.dsa.q = dsa_q; + pkt->pkt.public_cert->d.dsa.g = dsa_g; + pkt->pkt.public_cert->d.dsa.y = dsa_y; + } + else { + PKT_secret_cert *cert = pkt->pkt.secret_cert; + byte temp[8]; + + pkt->pkt.secret_cert->d.dsa.p = dsa_p; + pkt->pkt.secret_cert->d.dsa.q = dsa_q; + pkt->pkt.secret_cert->d.dsa.g = dsa_g; + pkt->pkt.secret_cert->d.dsa.y = dsa_y; + cert->d.dsa.protect.algo = iobuf_get_noeof(inp); pktlen--; + if( cert->d.dsa.protect.algo ) { + cert->d.dsa.is_protected = 1; + cert->d.dsa.protect.count = 0; + if( cert->d.dsa.protect.algo == 255 ) { + if( pktlen < 3 ) { + rc = G10ERR_INVALID_PACKET; + goto leave; + } + cert->d.dsa.protect.algo = iobuf_get_noeof(inp); pktlen--; + cert->d.dsa.protect.s2k = iobuf_get_noeof(inp); pktlen--; + cert->d.dsa.protect.hash = iobuf_get_noeof(inp); pktlen--; + switch( cert->d.dsa.protect.s2k ) { + case 1: + case 3: + for(i=0; i < 8 && pktlen; i++, pktlen-- ) + temp[i] = iobuf_get_noeof(inp); + memcpy(cert->d.dsa.protect.salt, temp, 8 ); + break; + } + switch( cert->d.dsa.protect.s2k ) { + case 0: if( list_mode ) printf( "\tsimple S2K" ); + break; + case 1: if( list_mode ) printf( "\tsalted S2K" ); + break; + case 3: if( list_mode ) printf( "\titer+salt S2K" ); + break; + default: + if( list_mode ) + printf( "\tunknown S2K %d\n", + cert->d.dsa.protect.s2k ); + rc = G10ERR_INVALID_PACKET; + goto leave; + } + + if( list_mode ) { + printf(", algo: %d, hash: %d", + cert->d.dsa.protect.algo, + cert->d.dsa.protect.hash ); + if( cert->d.dsa.protect.s2k == 1 + || cert->d.dsa.protect.s2k == 3 ) { + printf(", salt: "); + for(i=0; i < 8; i++ ) + printf("%02x", cert->d.dsa.protect.salt[i]); + } + putchar('\n'); + } + + if( cert->d.dsa.protect.s2k == 3 ) { + if( !pktlen ) { + rc = G10ERR_INVALID_PACKET; + goto leave; + } + cert->d.dsa.protect.count = iobuf_get_noeof(inp); + pktlen--; + } + + } + else { + if( list_mode ) + printf( "\tprotect algo: %d\n", + cert->d.dsa.protect.algo); + /* old version, we don't have a S2K, so we fake one */ + cert->d.dsa.protect.s2k = 0; + cert->d.dsa.protect.hash = DIGEST_ALGO_MD5; + } + if( pktlen < 8 ) { + rc = G10ERR_INVALID_PACKET; + goto leave; + } + for(i=0; i < 8 && pktlen; i++, pktlen-- ) + temp[i] = iobuf_get_noeof(inp); + if( list_mode ) { + printf( "\tprotect IV: "); + for(i=0; i < 8; i++ ) + printf(" %02x", temp[i] ); + putchar('\n'); + } + memcpy(cert->d.dsa.protect.iv, temp, 8 ); + } + else + cert->d.dsa.is_protected = 0; + /* It does not make sense to read it into secure memory. + * If the user is so careless, not to protect his secret key, + * we can assume, that he operates an open system :=(. + * So we put the key into secure memory when we unprotect him. */ + n = pktlen; cert->d.dsa.x = mpi_read(inp, &n, 0 ); pktlen -=n; + + cert->d.dsa.csum = read_16(inp); pktlen -= 2; + if( list_mode ) { + printf("\t[secret value x is not shown]\n" + "\tchecksum: %04hx\n", cert->d.dsa.csum); + } + /*log_mpidump("dsa p=", cert->d.dsa.p ); + log_mpidump("dsa q=", cert->d.dsa.q ); + log_mpidump("dsa g=", cert->d.dsa.g ); + log_mpidump("dsa y=", cert->d.dsa.y ); + log_mpidump("dsa x=", cert->d.dsa.x ); */ + } + } else if( algorithm == PUBKEY_ALGO_RSA ) { MPI rsa_pub_mod, rsa_pub_exp; @@ -1023,3 +1329,4 @@ parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt ) } + diff --git a/g10/plaintext.c b/g10/plaintext.c index 284b684b5..e15a269e8 100644 --- a/g10/plaintext.c +++ b/g10/plaintext.c @@ -30,6 +30,7 @@ #include "ttyio.h" #include "filter.h" #include "main.h" +#include "i18n.h" /**************** @@ -58,7 +59,8 @@ handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx ) fname[pt->namelen] = 0; } - if( !*fname ) { /* no filename given; write to stdout */ + if( !*fname || (*fname=='-' && !fname[1])) { + /* no filename or "-" given; write to stdout */ fp = stdout; } else if( overwrite_filep( fname ) ) @@ -181,3 +183,44 @@ ask_for_detached_datafile( md_filter_context_t *mfx, const char *inname ) } +/**************** + * Hash the given files and append the hash to hash context md. + * If FILES is NULL, hash stdin. + */ +int +hash_datafiles( MD_HANDLE md, STRLIST files, int textmode ) +{ + IOBUF fp; + STRLIST sl=NULL; + text_filter_context_t tfx; + int c; + + if( !files ) + add_to_strlist( &sl, "-"); + else + sl = files; + + for( ; sl; sl = sl->next ) { + fp = iobuf_open( sl->d ); + if( !fp ) { + log_error(_("can't open signed data '%s'\n"), + print_fname_stdin(sl->d)); + if( !files ) + free_strlist(sl); + return G10ERR_OPEN_FILE; + } + if( textmode ) { + memset( &tfx, 0, sizeof tfx); + iobuf_push_filter( fp, text_filter, &tfx ); + } + while( (c = iobuf_get(fp)) != -1 ) + md_putc(md, c ); + iobuf_close(fp); + } + + if( !files ) + free_strlist(sl); + return 0; +} + + diff --git a/g10/ringedit.c b/g10/ringedit.c index ba71f0e67..1ec35b811 100644 --- a/g10/ringedit.c +++ b/g10/ringedit.c @@ -517,6 +517,13 @@ keyring_search( PACKET *req, KBPOS *kbpos, IOBUF iobuf, const char *fname ) && !mpi_cmp( req_skc->d.elg.y, skc->d.elg.y ) && !mpi_cmp( req_skc->d.elg.x, skc->d.elg.x ) ) + || ( skc->pubkey_algo == PUBKEY_ALGO_DSA + && !mpi_cmp( req_skc->d.dsa.p, skc->d.dsa.p ) + && !mpi_cmp( req_skc->d.dsa.q, skc->d.dsa.q ) + && !mpi_cmp( req_skc->d.dsa.g, skc->d.dsa.g ) + && !mpi_cmp( req_skc->d.dsa.y, skc->d.dsa.y ) + && !mpi_cmp( req_skc->d.dsa.x, skc->d.dsa.x ) + ) || ( skc->pubkey_algo == PUBKEY_ALGO_RSA && !mpi_cmp( req_skc->d.rsa.rsa_n, skc->d.rsa.rsa_n ) && !mpi_cmp( req_skc->d.rsa.rsa_e, skc->d.rsa.rsa_e ) @@ -537,6 +544,12 @@ keyring_search( PACKET *req, KBPOS *kbpos, IOBUF iobuf, const char *fname ) && !mpi_cmp( req_pkc->d.elg.g, pkc->d.elg.g ) && !mpi_cmp( req_pkc->d.elg.y, pkc->d.elg.y ) ) + || ( pkc->pubkey_algo == PUBKEY_ALGO_DSA + && !mpi_cmp( req_pkc->d.dsa.p, pkc->d.dsa.p ) + && !mpi_cmp( req_pkc->d.dsa.q, pkc->d.dsa.q ) + && !mpi_cmp( req_pkc->d.dsa.g, pkc->d.dsa.g ) + && !mpi_cmp( req_pkc->d.dsa.y, pkc->d.dsa.y ) + ) || ( pkc->pubkey_algo == PUBKEY_ALGO_RSA && !mpi_cmp( req_pkc->d.rsa.rsa_n, pkc->d.rsa.rsa_n ) && !mpi_cmp( req_pkc->d.rsa.rsa_e, pkc->d.rsa.rsa_e ) diff --git a/g10/rsa.c b/g10/rsa.c index 23df05575..f1872b53a 100644 --- a/g10/rsa.c +++ b/g10/rsa.c @@ -77,9 +77,9 @@ g10_rsa_sign( PKT_secret_cert *skc, PKT_signature *sig, dp = md_read( md, digest_algo ); keyid_from_skc( skc, sig->keyid ); - sig->d.rsa.digest_algo = digest_algo; - sig->d.rsa.digest_start[0] = dp[0]; - sig->d.rsa.digest_start[1] = dp[1]; + sig->digest_algo = digest_algo; + sig->digest_start[0] = dp[0]; + sig->digest_start[1] = dp[1]; sig->d.rsa.rsa_integer = encode_md_value( md, mpi_get_nbits(skc->d.rsa.rsa_n)); skey.e = skc->d.rsa.rsa_e; diff --git a/g10/seckey-cert.c b/g10/seckey-cert.c index 85b0ed7af..f126ba01b 100644 --- a/g10/seckey-cert.c +++ b/g10/seckey-cert.c @@ -141,6 +141,7 @@ check_elg( PKT_secret_cert *cert ) return 0; } + static int protect_elg( PKT_secret_cert *cert, DEK *dek ) { @@ -174,6 +175,126 @@ protect_elg( PKT_secret_cert *cert, DEK *dek ) return 0; } +static int +check_dsa( PKT_secret_cert *cert ) +{ + byte *buffer; + u16 csum=0; + int res; + unsigned nbytes; + u32 keyid[2]; + DSA_secret_key skey; + char save_iv[8]; + + if( cert->d.dsa.is_protected ) { /* remove the protection */ + DEK *dek = NULL; + MPI test_x; + BLOWFISH_context *blowfish_ctx=NULL; + + switch( cert->d.dsa.protect.algo ) { + case CIPHER_ALGO_NONE: BUG(); break; + case CIPHER_ALGO_BLOWFISH: + keyid_from_skc( cert, keyid ); + if( cert->d.dsa.protect.s2k == 1 + || cert->d.dsa.protect.s2k == 3 ) + dek = get_passphrase_hash( keyid, NULL, + cert->d.dsa.protect.salt ); + else + dek = get_passphrase_hash( keyid, NULL, NULL ); + + blowfish_ctx = m_alloc_secure( sizeof *blowfish_ctx ); + blowfish_setkey( blowfish_ctx, dek->key, dek->keylen ); + m_free(dek); /* pw is in secure memory, so m_free() burns it */ + blowfish_setiv( blowfish_ctx, NULL ); + memcpy(save_iv, cert->d.dsa.protect.iv, 8 ); + blowfish_decode_cfb( blowfish_ctx, + cert->d.dsa.protect.iv, + cert->d.dsa.protect.iv, 8 ); + mpi_set_secure(cert->d.dsa.x ); + /*fixme: maybe it is better to set the buffer secure with a + * new get_buffer_secure() function */ + buffer = mpi_get_buffer( cert->d.dsa.x, &nbytes, NULL ); + csum = checksum_u16( nbytes*8 ); + blowfish_decode_cfb( blowfish_ctx, buffer, buffer, nbytes ); + csum += checksum( buffer, nbytes ); + test_x = mpi_alloc_secure( mpi_get_nlimbs(cert->d.dsa.x) ); + mpi_set_buffer( test_x, buffer, nbytes, 0 ); + m_free( buffer ); + m_free( blowfish_ctx ); + /* now let's see wether we have used the right passphrase */ + if( csum != cert->d.dsa.csum ) { + mpi_free(test_x); + memcpy( cert->d.dsa.protect.iv, save_iv, 8 ); + return G10ERR_BAD_PASS; + } + + skey.p = cert->d.dsa.p; + skey.q = cert->d.dsa.q; + skey.g = cert->d.dsa.g; + skey.y = cert->d.dsa.y; + skey.x = test_x; + res = dsa_check_secret_key( &skey ); + memset( &skey, 0, sizeof skey ); + if( !res ) { + mpi_free(test_x); + memcpy( cert->d.dsa.protect.iv, save_iv, 8 ); + return G10ERR_BAD_PASS; + } + mpi_set(cert->d.dsa.x, test_x); + mpi_free(test_x); + cert->d.dsa.is_protected = 0; + break; + + default: + return G10ERR_CIPHER_ALGO; /* unsupport protection algorithm */ + } + } + else { /* not protected */ + buffer = mpi_get_buffer( cert->d.dsa.x, &nbytes, NULL ); + csum = checksum_u16( nbytes*8 ); + csum += checksum( buffer, nbytes ); + m_free( buffer ); + if( csum != cert->d.dsa.csum ) + return G10ERR_CHECKSUM; + } + + return 0; +} + + +static int +protect_dsa( PKT_secret_cert *cert, DEK *dek ) +{ + byte *buffer; + unsigned nbytes; + + if( !cert->d.dsa.is_protected ) { /* add the protection */ + BLOWFISH_context *blowfish_ctx=NULL; + + switch( cert->d.dsa.protect.algo ) { + case CIPHER_ALGO_NONE: BUG(); break; + case CIPHER_ALGO_BLOWFISH: + blowfish_ctx = m_alloc_secure( sizeof *blowfish_ctx ); + blowfish_setkey( blowfish_ctx, dek->key, dek->keylen ); + blowfish_setiv( blowfish_ctx, NULL ); + blowfish_encode_cfb( blowfish_ctx, + cert->d.dsa.protect.iv, + cert->d.dsa.protect.iv, 8 ); + buffer = mpi_get_buffer( cert->d.dsa.x, &nbytes, NULL ); + blowfish_encode_cfb( blowfish_ctx, buffer, buffer, nbytes ); + mpi_set_buffer( cert->d.dsa.x, buffer, nbytes, 0 ); + m_free( buffer ); + m_free( blowfish_ctx ); + cert->d.dsa.is_protected = 1; + break; + + default: + return G10ERR_CIPHER_ALGO; /* unsupport protection algorithm */ + } + } + return 0; +} + #ifdef HAVE_RSA_CIPHER static int @@ -282,6 +403,8 @@ check_secret_key( PKT_secret_cert *cert ) log_error("Invalid passphrase; please try again ...\n"); if( cert->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) rc = check_elg( cert ); + else if( cert->pubkey_algo == PUBKEY_ALGO_DSA ) + rc = check_dsa( cert ); #ifdef HAVE_RSA_CIPHER else if( cert->pubkey_algo == PUBKEY_ALGO_RSA ) rc = check_rsa( cert ); @@ -303,6 +426,8 @@ is_secret_key_protected( PKT_secret_cert *cert ) { if( cert->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) return cert->d.elg.is_protected? cert->d.elg.protect.algo : 0; + else if( cert->pubkey_algo == PUBKEY_ALGO_DSA ) + return cert->d.dsa.is_protected? cert->d.dsa.protect.algo : 0; #ifdef HAVE_RSA_CIPHER else if( cert->pubkey_algo == PUBKEY_ALGO_RSA ) return cert->d.rsa.is_protected? cert->d.rsa.protect_algo : 0; @@ -323,6 +448,8 @@ protect_secret_key( PKT_secret_cert *cert, DEK *dek ) if( cert->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) return protect_elg( cert, dek ); + else if( cert->pubkey_algo == PUBKEY_ALGO_DSA ) + return protect_dsa( cert, dek ); else return G10ERR_PUBKEY_ALGO; } diff --git a/g10/sig-check.c b/g10/sig-check.c index 32371b359..f25f2e19f 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -66,11 +66,11 @@ do_check( PKT_public_cert *pkc, PKT_signature *sig, MD_HANDLE digest ) if( pkc->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) { ELG_public_key pkey; - if( (rc=check_digest_algo(sig->d.elg.digest_algo)) ) + if( (rc=check_digest_algo(sig->digest_algo)) ) goto leave; /* make sure the digest algo is enabled (in case of a detached * signature */ - md_enable( digest, sig->d.elg.digest_algo ); + md_enable( digest, sig->digest_algo ); /* complete the digest */ md_putc( digest, sig->sig_class ); { u32 a = sig->timestamp; @@ -87,6 +87,59 @@ do_check( PKT_public_cert *pkc, PKT_signature *sig, MD_HANDLE digest ) if( !elg_verify( sig->d.elg.a, sig->d.elg.b, result, &pkey ) ) rc = G10ERR_BAD_SIGN; } + else if( pkc->pubkey_algo == PUBKEY_ALGO_DSA ) { + DSA_public_key pkey; + + if( (rc=check_digest_algo(sig->digest_algo)) ) + goto leave; + /* make sure the digest algo is enabled (in case of a detached + * signature */ + md_enable( digest, sig->digest_algo ); + + assert( sig->digest_algo == DIGEST_ALGO_SHA1 ); + + /* 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 += 4; + } + else + n = 4; + /* 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 ); + log_hexdump("digest is: ", md_read(digest, DIGEST_ALGO_SHA1), 20); + result = encode_md_value( digest, mpi_get_nbits(pkc->d.dsa.p)); + pkey.p = pkc->d.dsa.p; + pkey.q = pkc->d.dsa.q; + pkey.g = pkc->d.dsa.g; + pkey.y = pkc->d.dsa.y; + if( !dsa_verify( sig->d.dsa.r, sig->d.dsa.s, result, &pkey ) ) + rc = G10ERR_BAD_SIGN; + } #ifdef HAVE_RSA_CIPHER else if( pkc->pubkey_algo == PUBKEY_ALGO_RSA ) { int i, j, c, old_enc; @@ -125,10 +178,10 @@ do_check( PKT_public_cert *pkc, PKT_signature *sig, MD_HANDLE digest ) goto leave; } - if( (rc=check_digest_algo(sig->d.rsa.digest_algo)) ) + if( (rc=check_digest_algo(sig->digest_algo)) ) goto leave; /* unsupported algo */ - md_enable( digest, sig->d.rsa.digest_algo ); - asn = md_asn_oid( sig->d.rsa.digest_algo, &asnlen, &mdlen ); + md_enable( digest, sig->digest_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-- ) @@ -142,7 +195,7 @@ do_check( PKT_public_cert *pkc, PKT_signature *sig, MD_HANDLE digest ) if( c != 0xff ) break; i++; - if( c != sig->d.rsa.digest_algo || mpi_getbyte(result, i) ) { + if( c != sig->digest_algo || mpi_getbyte(result, i) ) { /* Padding or leading bytes in signature is wrong */ rc = G10ERR_BAD_PUBKEY; goto leave; @@ -163,7 +216,7 @@ do_check( PKT_public_cert *pkc, PKT_signature *sig, MD_HANDLE digest ) md_putc( digest, a & 0xff ); } md_final( digest ); - dp = md_read( digest, sig->d.rsa.digest_algo ); + dp = md_read( digest, sig->digest_algo ); for(i=mdlen-1; i >= 0; i--, dp++ ) { if( mpi_getbyte( result, i ) != *dp ) { rc = G10ERR_BAD_SIGN; @@ -188,7 +241,7 @@ do_check( PKT_public_cert *pkc, PKT_signature *sig, MD_HANDLE digest ) /**************** * check the signature pointed to by NODE. This is a key signatures. - * If the function detects a elf signature, it uses the PKC from + * If the function detects a self-signature, it uses the PKC from * NODE and does not read the any public key. */ int @@ -210,9 +263,11 @@ check_key_signature( KBNODE root, KBNODE node, int *is_selfsig ) sig = node->pkt->pkt.signature; if( sig->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) - algo = sig->d.elg.digest_algo; + algo = sig->digest_algo; + else if( sig->pubkey_algo == PUBKEY_ALGO_DSA ) + algo = sig->digest_algo; else if(sig->pubkey_algo == PUBKEY_ALGO_RSA ) - algo = sig->d.rsa.digest_algo; + algo = sig->digest_algo; else return G10ERR_PUBKEY_ALGO; if( (rc=check_digest_algo(algo)) ) @@ -233,7 +288,18 @@ check_key_signature( KBNODE root, KBNODE node, int *is_selfsig ) keyid_from_pkc( pkc, keyid ); md = md_open( algo, 0 ); + if( sig->sig_class== 16 ) + md->debug = fopen("dsahashsig","w"); hash_public_cert( md, pkc ); + if( sig->version >=4 ) { + byte buf[5]; + buf[0] = 0xb4; /* indicates a userid packet */ + buf[1] = uid->len >> 24; /* but 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 ); if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) { if( is_selfsig ) @@ -243,6 +309,8 @@ check_key_signature( KBNODE root, KBNODE node, int *is_selfsig ) else rc = signature_check( sig, md ); md_close(md); + if( sig->sig_class== 16 ) + fclose(md->debug); } else { log_error("no user id for key signature packet\n"); diff --git a/g10/sign.c b/g10/sign.c index 9d8208870..4cf7b7bd6 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -50,6 +50,8 @@ complete_sig( PKT_signature *sig, PKT_secret_cert *skc, MD_HANDLE md ) ; else if( sig->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) g10_elg_sign( skc, sig, md, 0 ); + else if( sig->pubkey_algo == PUBKEY_ALGO_DSA ) + g10_dsa_sign( skc, sig, md, 0 ); else if( sig->pubkey_algo == PUBKEY_ALGO_RSA ) g10_rsa_sign( skc, sig, md, 0 ); else @@ -274,6 +276,8 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, if( sig->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) g10_elg_sign( skc, sig, md, DIGEST_ALGO_RMD160 ); + else if( sig->pubkey_algo == PUBKEY_ALGO_DSA ) + g10_dsa_sign( skc, sig, md, DIGEST_ALGO_SHA1 ); else if( sig->pubkey_algo == PUBKEY_ALGO_RSA ) g10_rsa_sign( skc, sig, md, DIGEST_ALGO_RMD160 ); else @@ -428,6 +432,8 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) if( sig->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) g10_elg_sign( skc, sig, md, DIGEST_ALGO_RMD160 ); + else if( sig->pubkey_algo == PUBKEY_ALGO_DSA ) + g10_dsa_sign( skc, sig, md, DIGEST_ALGO_SHA1 ); else if( sig->pubkey_algo == PUBKEY_ALGO_RSA ) g10_rsa_sign( skc, sig, md, DIGEST_ALGO_RMD160 ); else @@ -1066,6 +1072,7 @@ change_passphrase( const char *username ) break; } else { /* okay */ + /* FIXME: what about dsa */ skc->d.elg.protect.algo = CIPHER_ALGO_BLOWFISH; skc->d.elg.protect.s2k = 1; skc->d.elg.protect.hash = DIGEST_ALGO_RMD160; diff --git a/g10/status.c b/g10/status.c index 05f902a5f..5aa712a30 100644 --- a/g10/status.c +++ b/g10/status.c @@ -36,6 +36,12 @@ set_status_fd( int newfd ) void write_status( int no ) +{ + write_status_text( no, NULL ); +} + +void +write_status_text( int no, const char *text) { const char *s; @@ -53,7 +59,13 @@ write_status( int no ) default: s = "?\n"; break; } - write( fd, s, strlen(s) ); - + if( text ) { + write( fd, s, strlen(s)-1 ); + write( fd, " ", 1 ); + write( fd, text, strlen(text) ); + write( fd, "\n", 1 ); + } + else + write( fd, s, strlen(s) ); } diff --git a/g10/status.h b/g10/status.h index a3d493b89..41820825f 100644 --- a/g10/status.h +++ b/g10/status.h @@ -37,6 +37,7 @@ /*-- status.c --*/ void set_status_fd( int fd ); void write_status( int no ); +void write_status_text( int no, const char *text); #endif /*G10_STATUS_H*/ diff --git a/g10/trustdb.c b/g10/trustdb.c index 8c9088b91..257c37204 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -912,7 +912,7 @@ print_user_id( const char *text, u32 *keyid ) putchar(' '); } putchar('\"'); - print_string( stdout, p, n ); + print_string( stdout, p, n, 0 ); putchar('\"'); putchar('\n'); m_free(p); @@ -1520,13 +1520,18 @@ init_trustdb( int level, const char *dbname ) assert(p); *p = 0; if( access( fname, F_OK ) ) { - #if __MINGW32__ - if( mkdir( fname ) ) - #else - if( mkdir( fname, S_IRUSR|S_IWUSR|S_IXUSR ) ) - #endif - log_fatal("can't create directory '%s': %s\n", - fname, strerror(errno) ); + if( strlen(fname) >= 7 + && !strcmp(fname+strlen(fname)-7, "/.gnupg" ) ) { + #if __MINGW32__ + if( mkdir( fname ) ) + #else + if( mkdir( fname, S_IRUSR|S_IWUSR|S_IXUSR ) ) + #endif + log_fatal("can't create directory '%s': %s\n", + fname, strerror(errno) ); + } + else + log_fatal("directory '%s' does not exist!\n", fname ); } *p = '/'; create_db( fname ); @@ -1539,7 +1544,7 @@ init_trustdb( int level, const char *dbname ) return 0; /* we can verify a signature about our local data (secring and trustdb) - * in ~/.g10/ here */ + * in ~/.gnupg/ here */ rc = verify_private_data(); if( !rc ) { /* verify, that our own certificates are in the trustDB diff --git a/g10/verify.c b/g10/verify.c new file mode 100644 index 000000000..3398c2ed4 --- /dev/null +++ b/g10/verify.c @@ -0,0 +1,87 @@ +/* verify.c - verify signed data + * 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 + +#include "options.h" +#include "packet.h" +#include "errors.h" +#include "iobuf.h" +#include "keydb.h" +#include "memory.h" +#include "util.h" +#include "main.h" +#include "filter.h" +#include "ttyio.h" +#include "i18n.h" + + + +/**************** + * Assume that the input is a signature and verify it without + * generating any output. With no arguments, the sigature packet + * is read from stdin (it may be a detached signature when not + * used in batch mode). If only a sigfile is given, is maybe a complete + * signature or a detached signature in which case the signed stuff + * is expected from stdin. With more than 1 argument, the first should + * be a detached signature and the remaining files are the signed stuff. + */ + +int +verify_signatures( int nfiles, char **files ) +{ + IOBUF fp; + armor_filter_context_t afx; + const char *sigfile; + int i, rc; + STRLIST sl; + + sigfile = nfiles? *files : NULL; + + /* open the signature file */ + fp = iobuf_open(sigfile); + if( !fp ) { + log_error(_("can't open '%s'\n"), print_fname_stdin(sigfile)); + return G10ERR_OPEN_FILE; + } + + if( !opt.no_armor ) { + if( use_armor_filter( fp ) ) { + memset( &afx, 0, sizeof afx); + iobuf_push_filter( fp, armor_filter, &afx ); + } + } + + sl = NULL; + for(i=1 ; i < nfiles; i++ ) + add_to_strlist( &sl, files[i] ); + rc = proc_signature_packets( fp, sl ); + free_strlist(sl); + iobuf_close(fp); + return rc; +} + + + diff --git a/include/ChangeLog b/include/ChangeLog index e69de29bb..b0dd2b902 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -0,0 +1,9 @@ +Mon Mar 9 12:59:55 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.h: Included dsa.h. + +Tue Mar 3 15:11:21 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.h (random.h): Add new header and move all relevalt + functions to this header. + diff --git a/include/cipher.h b/include/cipher.h index d7a89ab46..9987fdec9 100644 --- a/include/cipher.h +++ b/include/cipher.h @@ -34,6 +34,8 @@ #endif #include "../cipher/blowfish.h" #include "../cipher/elgamal.h" +#include "../cipher/dsa.h" +#include "../cipher/random.h" #define CIPHER_ALGO_NONE 0 @@ -78,10 +80,6 @@ int check_cipher_algo( int algo ); int check_pubkey_algo( int algo ); int check_digest_algo( int algo ); -/*-- random.c --*/ -int quick_random_gen( int onoff ); -void randomize_buffer( byte *buffer, size_t length, int level ); -byte get_random_byte( int level ); /*-- smallprime.c --*/ extern ushort small_prime_numbers[]; diff --git a/include/errors.h b/include/errors.h index 36aff5bd6..b405d2eee 100644 --- a/include/errors.h +++ b/include/errors.h @@ -58,5 +58,6 @@ #define G10ERR_CLOSE_FILE 36 #define G10ERR_RENAME_FILE 37 #define G10ERR_DELETE_FILE 38 +#define G10ERR_UNEXPECTED 39 #endif /*G10_ERRORS_H*/ diff --git a/include/util.h b/include/util.h index e083c53c9..620457860 100644 --- a/include/util.h +++ b/include/util.h @@ -101,10 +101,13 @@ const char *strusage( int level ); /*-- fileutil.c --*/ char *make_filename( const char *first_part, ... ); +const char *print_fname_stdin( const char *s ); +const char *print_fname_stdout( const char *s ); + /*-- miscutil.c --*/ u32 make_timestamp(void); -void print_string( FILE *fp, byte *p, size_t n ); +void print_string( FILE *fp, byte *p, size_t n, int delim ); int answer_is_yes( const char *s ); /*-- strgutil.c --*/ diff --git a/util/ChangeLog b/util/ChangeLog index 169a5b96f..561b84905 100644 --- a/util/ChangeLog +++ b/util/ChangeLog @@ -1,3 +1,16 @@ +Sat Mar 7 11:54:35 1998 Werner Koch (wk@isil.d.shuttle.de) + + * miscutil.c (print_string): New arg delim; changed all callers. + +Thu Mar 5 12:19:30 1998 Werner Koch (wk@isil.d.shuttle.de) + + * errors.c: New strings. + +Thu Mar 5 12:06:31 1998 Werner Koch (wk@isil.d.shuttle.de) + + * iobuf.c (iobuf_open): A name of "-" now opens stdin. + * fileutil.c (print_fname_stdout, print_fname_stdin): New. + Fri Feb 27 10:20:03 1998 Werner Koch (wk@isil.d.shuttle.de) * memory.c (m_is_secure): Removed. diff --git a/util/errors.c b/util/errors.c index 28a418536..35b55177c 100644 --- a/util/errors.c +++ b/util/errors.c @@ -64,9 +64,13 @@ g10_errstr( int err ) X(NI_PUBKEY ,"Unimplemented pubkey algorithm") X(NI_CIPHER ,"Unimplemented cipher algorithm") X(SIG_CLASS ,"Unknown signature class") - X(TRUSTDB ,"TrustDB error") + X(TRUSTDB ,"Trust database error") X(BAD_CERT ,"Bad certificate") - X(INV_USER_ID ,"malformed user id") + X(INV_USER_ID ,"Malformed user id") + X(CLOSE_FILE ,"File close error") + X(RENAME_FILE ,"File rename error") + X(DELETE_FILE ,"File delete error") + X(UNEXPECTED ,"Unexpected data") default: p = buf; sprintf(buf, "g10err=%d", err); break; } diff --git a/util/fileutil.c b/util/fileutil.c index e5f9311bc..f3e00f8d6 100644 --- a/util/fileutil.c +++ b/util/fileutil.c @@ -64,3 +64,26 @@ make_filename( const char *first_part, ... ) return name; } + +/**************** + * A simple function to decide, wether the filename ist stdout + * or a real filename. + */ +const char * +print_fname_stdout( const char *s ) +{ + if( !s || (*s == '-' && !s[1]) ) + return "[stdout]"; + return s; +} + + +const char * +print_fname_stdin( const char *s ) +{ + if( !s || (*s == '-' && !s[1]) ) + return "[stdin]"; + return s; +} + + diff --git a/util/iobuf.c b/util/iobuf.c index ca3398674..30d6186fd 100644 --- a/util/iobuf.c +++ b/util/iobuf.c @@ -371,7 +371,7 @@ iobuf_open( const char *fname ) file_filter_ctx_t *fcx; size_t len; - if( !fname ) { + if( !fname || (*fname=='-' && !fname[1]) ) { fp = stdin; /* fixme: set binary mode for msdoze */ fname = "[stdin]"; } diff --git a/util/miscutil.c b/util/miscutil.c index 9dbb13ac1..cad89e8ab 100644 --- a/util/miscutil.c +++ b/util/miscutil.c @@ -37,13 +37,21 @@ make_timestamp() * Print a string to FP, but filter all control characters out. */ void -print_string( FILE *fp, byte *p, size_t n ) +print_string( FILE *fp, byte *p, size_t n, int delim ) { for( ; n; n--, p++ ) - if( iscntrl( *p ) ) { + if( iscntrl( *p ) || *p == delim ) { putc('\\', fp); if( *p == '\n' ) putc('n', fp); + else if( *p == '\r' ) + putc('r', fp); + else if( *p == '\f' ) + putc('f', fp); + else if( *p == '\v' ) + putc('v', fp); + else if( *p == '\b' ) + putc('b', fp); else if( !*p ) putc('0', fp); else