diff options
author | Werner Koch <[email protected]> | 2010-04-21 16:26:17 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2010-04-21 16:26:17 +0000 |
commit | a1412b05debe693e6aabaf2c2c337bc33f7dfd41 (patch) | |
tree | 214dc8928b73aaa385d69eaa180164318ff0bb93 /agent | |
parent | Disable card support for now (diff) | |
download | gnupg-a1412b05debe693e6aabaf2c2c337bc33f7dfd41.tar.gz gnupg-a1412b05debe693e6aabaf2c2c337bc33f7dfd41.zip |
More changes on the way to remove secring.gpg.
Diffstat (limited to 'agent')
-rw-r--r-- | agent/ChangeLog | 10 | ||||
-rw-r--r-- | agent/agent.h | 1 | ||||
-rw-r--r-- | agent/command.c | 5 | ||||
-rw-r--r-- | agent/findkey.c | 154 | ||||
-rw-r--r-- | agent/pksign.c | 108 |
5 files changed, 228 insertions, 50 deletions
diff --git a/agent/ChangeLog b/agent/ChangeLog index 46e0b6fe3..6611a35ad 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,13 @@ +2010-04-19 Werner Koch <[email protected]> + + * pksign.c (get_dsa_qbits, do_encode_dsa): New. + (agent_pksign_do): Detect DSA keys and use do_encode_dsa. + * findkey.c (agent_public_key_from_file): Factor some code out to .. + (key_parms_from_sexp): New. + (agent_is_dsa_key): New. + + * command.c (cmd_sethash): Clear digeest.RAW_VALUE. + 2010-04-14 Werner Koch <[email protected]> * Makefile.am (libexec_PROGRAMS) [W32CE]: Do not build diff --git a/agent/agent.h b/agent/agent.h index ea0d49465..3f0c19561 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -232,6 +232,7 @@ gpg_error_t agent_key_from_file (ctrl_t ctrl, gpg_error_t agent_public_key_from_file (ctrl_t ctrl, const unsigned char *grip, gcry_sexp_t *result); +int agent_is_dsa_key (gcry_sexp_t s_key); int agent_key_available (const unsigned char *grip); gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, int *r_keytype, diff --git a/agent/command.c b/agent/command.c index 1e0c5e744..b78dc07d9 100644 --- a/agent/command.c +++ b/agent/command.c @@ -589,7 +589,7 @@ cmd_setkeydesc (assuan_context_t ctx, char *line) static const char hlp_sethash[] = - "SETHASH --hash=<name>|<algonumber> <hexstring>\n" + "SETHASH (--hash=<name>)|(<algonumber>) <hexstring>\n" "\n" "The client can use this command to tell the server about the data\n" "(which usually is a hash) to be signed."; @@ -642,6 +642,7 @@ cmd_sethash (assuan_context_t ctx, char *line) return set_error (GPG_ERR_UNSUPPORTED_ALGORITHM, NULL); } ctrl->digest.algo = algo; + ctrl->digest.raw_value = 0; /* Parse the hash value. */ n = 0; @@ -848,7 +849,7 @@ static const char hlp_keyinfo[] = "\n" "TYPE is describes the type of the key:\n" " 'D' - Regular key stored on disk,\n" - " 'T' - Key is stored on a smartcard (token).\n" + " 'T' - Key is stored on a smartcard (token),\n" " '-' - Unknown type.\n" "\n" "SERIALNO is an ASCII string with the serial number of the\n" diff --git a/agent/findkey.c b/agent/findkey.c index 30aa7c938..d6478ac4d 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -1,6 +1,6 @@ /* findkey.c - Locate the secret key - * Copyright (C) 2001, 2002, 2003, 2004, 2005, - * 2007 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, + * 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -626,50 +626,32 @@ agent_key_from_file (ctrl_t ctrl, const char *desc_text, } - -/* Return the public key for the keygrip GRIP. The result is stored - at RESULT. This function extracts the public key from the private - key database. On failure an error code is returned and NULL stored - at RESULT. */ -gpg_error_t -agent_public_key_from_file (ctrl_t ctrl, - const unsigned char *grip, - gcry_sexp_t *result) +/* Return the string name from the S-expression S_KEY as well as a + string describing the names of the parameters. ALGONAMESIZE and + ELEMSSIZE give the allocated size of the provided buffers. The + buffers may be NULL if not required. If R_LIST is not NULL the top + level list will be stored tehre; the caller needs to release it in + this case. */ +static gpg_error_t +key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list, + char *r_algoname, size_t algonamesize, + char *r_elems, size_t elemssize) { - int i, idx, rc; - gcry_sexp_t s_skey; - const char *algoname; - gcry_sexp_t uri_sexp, comment_sexp; - const char *uri, *comment; - size_t uri_length, comment_length; - char *format, *p; - void *args[4+2+2+1]; /* Size is max. # of elements + 2 for uri + 2 - for comment + end-of-list. */ - int argidx; gcry_sexp_t list, l2; - const char *name; - const char *s; + const char *name, *algoname, *elems; size_t n; - const char *elems; - gcry_mpi_t *array; - - (void)ctrl; - - *result = NULL; - rc = read_key_file (grip, &s_skey); - if (rc) - return rc; + if (r_list) + *r_list = NULL; - list = gcry_sexp_find_token (s_skey, "shadowed-private-key", 0 ); + list = gcry_sexp_find_token (s_key, "shadowed-private-key", 0 ); if (!list) - list = gcry_sexp_find_token (s_skey, "protected-private-key", 0 ); + list = gcry_sexp_find_token (s_key, "protected-private-key", 0 ); if (!list) - list = gcry_sexp_find_token (s_skey, "private-key", 0 ); + list = gcry_sexp_find_token (s_key, "private-key", 0 ); if (!list) { log_error ("invalid private key format\n"); - gcry_sexp_release (s_skey); return gpg_error (GPG_ERR_BAD_SECKEY); } @@ -696,19 +678,99 @@ agent_public_key_from_file (ctrl_t ctrl, { log_error ("unknown private key algorithm\n"); gcry_sexp_release (list); - gcry_sexp_release (s_skey); return gpg_error (GPG_ERR_BAD_SECKEY); } + if (r_algoname) + { + if (strlen (algoname) >= algonamesize) + return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); + strcpy (r_algoname, algoname); + } + if (r_elems) + { + if (strlen (elems) >= elemssize) + return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); + strcpy (r_elems, elems); + } + + if (r_list) + *r_list = list; + else + gcry_sexp_release (list); + + return 0; +} + + +/* Return true if S_KEY is a DSA style key. */ +int +agent_is_dsa_key (gcry_sexp_t s_key) +{ + char algoname[6]; + + if (!s_key) + return 0; + + if (key_parms_from_sexp (s_key, NULL, algoname, sizeof algoname, NULL, 0)) + return 0; /* Error - assume it is not an DSA key. */ + + return (!strcmp (algoname, "dsa") || !strcmp (algoname, "ecdsa")); +} + + + +/* Return the public key for the keygrip GRIP. The result is stored + at RESULT. This function extracts the public key from the private + key database. On failure an error code is returned and NULL stored + at RESULT. */ +gpg_error_t +agent_public_key_from_file (ctrl_t ctrl, + const unsigned char *grip, + gcry_sexp_t *result) +{ + gpg_error_t err; + int i, idx; + gcry_sexp_t s_skey; + char algoname[6]; + char elems[6]; + gcry_sexp_t uri_sexp, comment_sexp; + const char *uri, *comment; + size_t uri_length, comment_length; + char *format, *p; + void *args[4+2+2+1]; /* Size is max. # of elements + 2 for uri + 2 + for comment + end-of-list. */ + int argidx; + gcry_sexp_t list, l2; + const char *s; + gcry_mpi_t *array; + + (void)ctrl; + + *result = NULL; + + err = read_key_file (grip, &s_skey); + if (err) + return err; + + err = key_parms_from_sexp (s_skey, &list, + algoname, sizeof algoname, + elems, sizeof elems); + if (err) + { + gcry_sexp_release (s_skey); + return err; + } + /* Allocate an array for the parameters and copy them out of the secret key. FIXME: We should have a generic copy function. */ array = xtrycalloc (strlen(elems) + 1, sizeof *array); if (!array) { - rc = gpg_error_from_syserror (); + err = gpg_error_from_syserror (); gcry_sexp_release (list); gcry_sexp_release (s_skey); - return rc; + return err; } for (idx=0, s=elems; *s; s++, idx++ ) @@ -757,8 +819,8 @@ agent_public_key_from_file (ctrl_t ctrl, /* FIXME: The following thing is pretty ugly code; we should - investigate how to make it cleaner. Probably code to handle - canonical S-expressions in a memory buffer is better suioted for + investigate how to make it cleaner. Probably code to handle + canonical S-expressions in a memory buffer is better suited for such a task. After all that is what we do in protect.c. Neeed to find common patterns and write a straightformward API to use them. */ @@ -767,13 +829,13 @@ agent_public_key_from_file (ctrl_t ctrl, format = xtrymalloc (15+7*strlen (elems)+10+15+1+1); if (!format) { - rc = gpg_error_from_syserror (); + err = gpg_error_from_syserror (); for (i=0; array[i]; i++) gcry_mpi_release (array[i]); xfree (array); gcry_sexp_release (uri_sexp); gcry_sexp_release (comment_sexp); - return rc; + return err; } argidx = 0; @@ -806,7 +868,7 @@ agent_public_key_from_file (ctrl_t ctrl, assert (argidx < DIM (args)); args[argidx] = NULL; - rc = gcry_sexp_build_array (&list, NULL, format, args); + err = gcry_sexp_build_array (&list, NULL, format, args); xfree (format); for (i=0; array[i]; i++) gcry_mpi_release (array[i]); @@ -814,9 +876,9 @@ agent_public_key_from_file (ctrl_t ctrl, gcry_sexp_release (uri_sexp); gcry_sexp_release (comment_sexp); - if (!rc) + if (!err) *result = list; - return rc; + return err; } 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, |