diff options
Diffstat (limited to 'agent/pksign.c')
-rw-r--r-- | agent/pksign.c | 108 |
1 files changed, 106 insertions, 2 deletions
diff --git a/agent/pksign.c b/agent/pksign.c index 25cadb29e..7ae50a931 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -1,5 +1,5 @@ /* pksign.c - public key signing (well, actually using a secret key) - * Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2003, 2004, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -28,6 +28,7 @@ #include <sys/stat.h> #include "agent.h" +#include "i18n.h" static int @@ -75,6 +76,104 @@ do_encode_md (const byte * md, size_t mdlen, int algo, gcry_sexp_t * r_hash, } +/* Return the number of bits of the Q parameter from the DSA key + KEY. */ +static unsigned int +get_dsa_qbits (gcry_sexp_t key) +{ + gcry_sexp_t l1, l2; + gcry_mpi_t q; + unsigned int nbits; + + l1 = gcry_sexp_find_token (key, "private-key", 0); + if (!l1) + l1 = gcry_sexp_find_token (key, "protected-private-key", 0); + if (!l1) + l1 = gcry_sexp_find_token (key, "shadowed-private-key", 0); + if (!l1) + l1 = gcry_sexp_find_token (key, "public-key", 0); + if (!l1) + return 0; /* Does not contain a key object. */ + l2 = gcry_sexp_cadr (l1); + gcry_sexp_release (l1); + l1 = gcry_sexp_find_token (l2, "q", 1); + gcry_sexp_release (l2); + if (!l1) + return 0; /* Invalid object. */ + q = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l1); + if (!q) + return 0; /* Missing value. */ + nbits = gcry_mpi_get_nbits (q); + gcry_mpi_release (q); + + return nbits; +} + + +/* Encode a message digest for use with an DSA algorithm. */ +static gpg_error_t +do_encode_dsa (const byte * md, size_t mdlen, int dsaalgo, gcry_sexp_t pkey, + gcry_sexp_t *r_hash) +{ + gpg_error_t err; + gcry_sexp_t hash; + unsigned int qbits; + + *r_hash = NULL; + + if (dsaalgo == GCRY_PK_ECDSA) + qbits = gcry_pk_get_nbits (pkey); + else if (dsaalgo == GCRY_PK_DSA) + qbits = get_dsa_qbits (pkey); + else + return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); + + if ((qbits%8)) + { + log_error (_("DSA requires the hash length to be a" + " multiple of 8 bits\n")); + return gpg_error (GPG_ERR_INV_LENGTH); + } + + /* Don't allow any Q smaller than 160 bits. 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 (qbits < 160) + { + log_error (_("%s key uses an unsafe (%u bit) hash\n"), + gcry_pk_algo_name (dsaalgo), qbits); + return gpg_error (GPG_ERR_INV_LENGTH); + } + + /* Check if we're too short. Too long is safe as we'll + automatically left-truncate. */ + if (mdlen < qbits/8) + { + log_error (_("a %zu bit hash is not valid for a %u bit %s key\n"), + mdlen*8, + gcry_pk_get_nbits (pkey), + gcry_pk_algo_name (dsaalgo)); + /* FIXME: we need to check the requirements for ECDSA. */ + if (mdlen < 20 || dsaalgo == GCRY_PK_DSA) + return gpg_error (GPG_ERR_INV_LENGTH); + } + + /* Truncate. */ + if (mdlen > qbits/8) + mdlen = qbits/8; + + /* Create the S-expression. */ + err = gcry_sexp_build (&hash, NULL, + "(data (flags raw) (value %b))", + (int)mdlen, md); + if (!err) + *r_hash = hash; + return err; +} + + /* Special version of do_encode_md to take care of pkcs#1 padding. For TLS-MD5SHA1 we need to do the padding ourself as Libgrypt does not know about this special scheme. Fixme: We should have a @@ -180,8 +279,8 @@ agent_pksign_do (ctrl_t ctrl, const char *desc_text, else { /* No smartcard, but a private key */ - gcry_sexp_t s_hash = NULL; + int dsaalgo; /* Put the hash into a sexp */ if (ctrl->digest.algo == MD_USER_TLS_MD5SHA1) @@ -189,6 +288,11 @@ agent_pksign_do (ctrl_t ctrl, const char *desc_text, ctrl->digest.valuelen, gcry_pk_get_nbits (s_skey), &s_hash); + else if ( (dsaalgo = agent_is_dsa_key (s_skey)) ) + rc = do_encode_dsa (ctrl->digest.value, + ctrl->digest.valuelen, + dsaalgo, s_skey, + &s_hash); else rc = do_encode_md (ctrl->digest.value, ctrl->digest.valuelen, |