From 92e1528bf2206b44b7d321e686ac9a1c1251fc5b Mon Sep 17 00:00:00 2001 From: David Shaw Date: Thu, 30 Mar 2006 19:20:59 +0000 Subject: [PATCH] * main.h, seskey.c (encode_md_value): Modify to allow a q size greater than 160 bits as per DSA2. This will allow us to verify and issue DSA2 signatures for some backwards compatibility once we start generating DSA2 keys. * sign.c (do_sign), sig-check.c (do_check): Change all callers. * sign.c (do_sign): Enforce the 160-bit check for new signatures here since encode_md_value can handle non-160-bit digests now. This will need to come out once the standard for DSA2 is firmed up. --- g10/ChangeLog | 13 +++++++ g10/main.h | 4 +-- g10/seskey.c | 90 +++++++++++++++++++++++++++++++++++-------------- g10/sig-check.c | 3 +- g10/sign.c | 17 +++++++--- 5 files changed, 93 insertions(+), 34 deletions(-) diff --git a/g10/ChangeLog b/g10/ChangeLog index 18c729975..750c1b31a 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,16 @@ +2006-03-30 David Shaw + + * main.h, seskey.c (encode_md_value): Modify to allow a q size + greater than 160 bits as per DSA2. This will allow us to verify + and issue DSA2 signatures for some backwards compatibility once we + start generating DSA2 keys. + * sign.c (do_sign), sig-check.c (do_check): Change all callers. + + * sign.c (do_sign): Enforce the 160-bit check for new signatures + here since encode_md_value can handle non-160-bit digests now. + This will need to come out once the standard for DSA2 is firmed + up. + 2006-03-22 David Shaw * getkey.c (parse_auto_key_locate): Silently strip out duplicates diff --git a/g10/main.h b/g10/main.h index ac337fac9..434ecc2e8 100644 --- a/g10/main.h +++ b/g10/main.h @@ -203,8 +203,8 @@ void try_make_homedir( const char *fname ); /*-- seskey.c --*/ void make_session_key( DEK *dek ); MPI encode_session_key( DEK *dek, unsigned nbits ); -MPI encode_md_value( int pubkey_algo, MD_HANDLE md, - int hash_algo, unsigned nbits ); +MPI encode_md_value( PKT_public_key *pk, PKT_secret_key *sk, + MD_HANDLE md, int hash_algo ); /*-- import.c --*/ int parse_import_options(char *str,unsigned int *options,int noisy); diff --git a/g10/seskey.c b/g10/seskey.c index adf85c97f..9685bf5a6 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -195,36 +195,76 @@ do_encode_md( MD_HANDLE md, int algo, size_t len, unsigned nbits, /**************** * Encode a message digest into an MPI. - * v3compathack is used to work around a bug in old GnuPG versions - * which did put the algo identifier inseatd of the block type 1 into - * the encoded value. Setting this flag forces the old behaviour. + * If it's for a DSA signature, make sure that the hash is large + * enough to fill up q. If the hash is too big, take the leftmost + * bits. */ MPI -encode_md_value( int pubkey_algo, MD_HANDLE md, - int hash_algo, unsigned nbits ) +encode_md_value( PKT_public_key *pk, PKT_secret_key *sk, + MD_HANDLE md, int hash_algo ) { - int algo = hash_algo? hash_algo : md_get_algo(md); - const byte *asn; - size_t asnlen, mdlen; - MPI frame; + MPI frame; - if( pubkey_algo == PUBKEY_ALGO_DSA ) { - mdlen = md_digest_length (hash_algo); - if (mdlen != 20) { - log_error (_("DSA requires the use of a 160 bit hash algorithm\n")); - return NULL; - } + assert(hash_algo); + assert(pk || sk); - frame = md_is_secure(md)? mpi_alloc_secure((md_digest_length(hash_algo) - +BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB ) - : mpi_alloc((md_digest_length(hash_algo) - +BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB ); - mpi_set_buffer( frame, md_read(md, hash_algo), - md_digest_length(hash_algo), 0 ); + if((pk?pk->pubkey_algo:sk->pubkey_algo) == PUBKEY_ALGO_DSA) + { + /* It's a DSA signature, so find out the size of q. */ + + unsigned int qbytes=mpi_get_nbits(pk?pk->pkey[1]:sk->skey[1]); + + /* Make sure it is a multiple of 8 bits. */ + + if(qbytes%8) + { + log_error(_("DSA requires the hash length to be a" + " multiple of 8 bits\n")); + return NULL; + } + + /* Don't allow any q smaller than 160 bits. This might need a + revisit as the DSA2 design firms up, but for now, we don't + want someone to issue signatures from a key with a 16-bit q + or something like that, which would look correct but allow + trivial forgeries. Yes, I know this rules out using MD5 with + DSA. ;) */ + + if(qbytes<160) + { + log_error(_("DSA key %s uses an unsafe (%u bit) hash\n"), + pk?keystr_from_pk(pk):keystr_from_sk(sk),qbytes); + return NULL; + } + + qbytes/=8; + + /* Check if we're too short. Too long is safe as we'll + automatically left-truncate. */ + + if(md_digest_length(hash_algo) < qbytes) + { + log_error(_("DSA key %s requires a %u bit or larger hash\n"), + pk?keystr_from_pk(pk):keystr_from_sk(sk),qbytes*8); + return NULL; + } + + frame = md_is_secure(md)? mpi_alloc_secure((qbytes+BYTES_PER_MPI_LIMB-1) + / BYTES_PER_MPI_LIMB ) + : mpi_alloc((qbytes+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB ); + + mpi_set_buffer( frame, md_read(md, hash_algo), qbytes, 0 ); } - else { - asn = md_asn_oid( algo, &asnlen, &mdlen ); - frame = do_encode_md( md, algo, mdlen, nbits, asn, asnlen ); + else + { + const byte *asn; + size_t asnlen,mdlen; + + asn = md_asn_oid( hash_algo, &asnlen, &mdlen ); + frame = do_encode_md( md, hash_algo, mdlen, + mpi_get_nbits(pk?pk->pkey[0]:sk->skey[0]), + asn, asnlen ); } - return frame; + + return frame; } diff --git a/g10/sig-check.c b/g10/sig-check.c index d576769da..15703346c 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -274,8 +274,7 @@ do_check( PKT_public_key *pk, PKT_signature *sig, MD_HANDLE digest, } md_final( digest ); - result = encode_md_value( pk->pubkey_algo, digest, sig->digest_algo, - mpi_get_nbits(pk->pkey[0]) ); + result = encode_md_value( pk, NULL, digest, sig->digest_algo ); if (!result) return G10ERR_GENERAL; ctx.sig = sig; diff --git a/g10/sign.c b/g10/sign.c index 79e756753..925fef461 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -319,8 +319,17 @@ do_sign( PKT_secret_key *sk, PKT_signature *sig, } else { - frame = encode_md_value( sk->pubkey_algo, md, - digest_algo, mpi_get_nbits(sk->skey[0]) ); + /* TODO: remove this check in the future once all the + variable-q DSA stuff makes it into the standard. */ + if(!opt.expert + && sk->pubkey_algo==PUBKEY_ALGO_DSA + && md_digest_length(digest_algo)!=20) + { + log_error(_("DSA requires the use of a 160 bit hash algorithm\n")); + return G10ERR_GENERAL; + } + + frame = encode_md_value( NULL, sk, md, digest_algo ); if (!frame) return G10ERR_GENERAL; rc = pubkey_sign( sk->pubkey_algo, sig->data, frame, sk->skey ); @@ -336,9 +345,7 @@ do_sign( PKT_secret_key *sk, PKT_signature *sig, if( get_pubkey( pk, sig->keyid ) ) rc = G10ERR_NO_PUBKEY; else { - frame = encode_md_value (pk->pubkey_algo, md, - sig->digest_algo, - mpi_get_nbits(pk->pkey[0]) ); + frame = encode_md_value (pk, NULL, md, sig->digest_algo ); if (!frame) rc = G10ERR_GENERAL; else