diff options
author | Werner Koch <[email protected]> | 2002-01-31 16:38:45 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2002-01-31 16:38:45 +0000 |
commit | 7d9ed16fe6bfbb80755b2dba983e58435088bc63 (patch) | |
tree | 76ff1323a1d60163dca3d2de431eaa3cbeac9e64 | |
parent | A few test certificates (diff) | |
download | gnupg-7d9ed16fe6bfbb80755b2dba983e58435088bc63.tar.gz gnupg-7d9ed16fe6bfbb80755b2dba983e58435088bc63.zip |
* genkey.c (store_key): Protect the key.
(agent_genkey): Ask for the passphrase.
* findkey.c (unprotect): Actually unprotect the key.
* query.c (agent_askpin): Add an optional start_err_text.
-rw-r--r-- | agent/ChangeLog | 13 | ||||
-rw-r--r-- | agent/Makefile.am | 8 | ||||
-rw-r--r-- | agent/agent.h | 12 | ||||
-rw-r--r-- | agent/cache.c | 4 | ||||
-rw-r--r-- | agent/findkey.c | 61 | ||||
-rw-r--r-- | agent/genkey.c | 72 | ||||
-rw-r--r-- | agent/keyformat.txt | 73 | ||||
-rw-r--r-- | agent/protect-tool.c | 355 | ||||
-rw-r--r-- | agent/protect.c | 861 | ||||
-rw-r--r-- | agent/query.c | 14 |
10 files changed, 1409 insertions, 64 deletions
diff --git a/agent/ChangeLog b/agent/ChangeLog index 9a42b4053..21f496628 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,16 @@ +2002-01-31 Werner Koch <[email protected]> + + * genkey.c (store_key): Protect the key. + (agent_genkey): Ask for the passphrase. + * findkey.c (unprotect): Actually unprotect the key. + * query.c (agent_askpin): Add an optional start_err_text. + +2002-01-30 Werner Koch <[email protected]> + + * protect.c: New. + (hash_passphrase): Based on the GnuPG 1.0.6 version. + * protect-tool.c: New + 2002-01-29 Werner Koch <[email protected]> * findkey.c (agent_key_available): New. diff --git a/agent/Makefile.am b/agent/Makefile.am index 013862e48..eb5fa7d9d 100644 --- a/agent/Makefile.am +++ b/agent/Makefile.am @@ -19,6 +19,7 @@ ## Process this file with automake to produce Makefile.in bin_PROGRAMS = gpg-agent +noinst_PROGRAMS = protect-tool AM_CPPFLAGS = -I$(top_srcdir)/common $(LIBGCRYPT_CFLAGS) LDFLAGS = @LDFLAGS@ @@ -33,11 +34,16 @@ gpg_agent_SOURCES = \ pksign.c \ pkdecrypt.c \ genkey.c \ + protect.c \ trustlist.c gpg_agent_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a \ ../common/libcommon.a $(LIBGCRYPT_LIBS) +protect_tool_SOURCES = \ + protect-tool.c \ + protect.c - +protect_tool_LDADD = ../jnlib/libjnlib.a \ + ../common/libcommon.a $(LIBGCRYPT_LIBS) diff --git a/agent/agent.h b/agent/agent.h index d999bdad8..955dd1ce2 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -84,7 +84,7 @@ struct pin_entry_info_s { /*-- gpg-agent.c --*/ -void agent_exit (int rc); +void agent_exit (int rc); /* also implemented in other tools */ /*-- trans.c --*/ const char *trans (const char *text); @@ -97,7 +97,8 @@ GCRY_SEXP agent_key_from_file (const unsigned char *grip); int agent_key_available (const unsigned char *grip); /*-- query.c --*/ -int agent_askpin (const char *desc_text, struct pin_entry_info_s *pininfo); +int agent_askpin (const char *desc_text, const char *err_text, + struct pin_entry_info_s *pininfo); int agent_get_passphrase (char **retpass, const char *desc, const char *prompt, const char *errtext); @@ -119,6 +120,13 @@ int agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen, int agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparmlen, FILE *outfp); +/*-- protect.c --*/ +int agent_protect (const unsigned char *plainkey, const char *passphrase, + unsigned char **result, size_t *resultlen); +int agent_unprotect (const unsigned char *protectedkey, const char *passphrase, + unsigned char **result, size_t *resultlen); + + /*-- trustlist.c --*/ int agent_istrusted (const char *fpr); int agent_listtrusted (void *assuan_context); diff --git a/agent/cache.c b/agent/cache.c index 96213712d..aa7a21b16 100644 --- a/agent/cache.c +++ b/agent/cache.c @@ -132,7 +132,7 @@ housekeeping (void) /* Store DATA of length DATALEN in the cache under KEY and mark it - with a maxiumum lifetime of TTL seconds. If tehre is already data + with a maximum lifetime of TTL seconds. If tehre is already data under this key, it will be replaced. Using a DATA of NULL deletes the entry */ int @@ -206,7 +206,7 @@ agent_get_cache (const char *key) { if (r->pw && !strcmp (r->key, key)) { - /* put_cache does onlu put strings into the cache, so we + /* put_cache does only put strings into the cache, so we don't need the lengths */ r->accessed = time (NULL); return r->pw->data; diff --git a/agent/findkey.c b/agent/findkey.c index 50f832be6..097903340 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -31,24 +31,39 @@ #include "agent.h" static int -unprotect (GCRY_SEXP s_skey) +unprotect (unsigned char **keybuf) { struct pin_entry_info_s *pi; int rc; + unsigned char *result; + size_t resultlen; + int tries = 0; /* fixme: check whether the key needs unprotection */ - /* fixme: allocate the pin in secure memory */ - pi = xtrycalloc (1, sizeof (*pi) + 100); + pi = gcry_calloc_secure (1, sizeof (*pi) + 100); pi->max_length = 100; - pi->min_digits = 4; + pi->min_digits = 0; /* we want a real passphrase */ pi->max_digits = 8; pi->max_tries = 3; - rc = agent_askpin (NULL, pi); - /* fixme: actually unprotect the key and ask again until we get a valid - PIN - agent_askpin takes care of counting failed tries */ - + do + { + rc = agent_askpin (NULL, NULL, pi); + if (!rc) + { + rc = agent_unprotect (*keybuf, pi->pin, &result, &resultlen); + if (!rc) + { + xfree (*keybuf); + *keybuf = result; + xfree (pi); + return 0; + } + } + } + while ((rc == GNUPG_Bad_Passphrase || rc == GNUPG_Bad_PIN) + && tries++ < 3); xfree (pi); return rc; } @@ -64,8 +79,8 @@ agent_key_from_file (const unsigned char *grip) char *fname; FILE *fp; struct stat st; - char *buf; - size_t buflen, erroff; + unsigned char *buf; + size_t len, buflen, erroff; GCRY_SEXP s_skey; char hexgrip[41]; @@ -111,13 +126,35 @@ agent_key_from_file (const unsigned char *grip) (unsigned int)erroff, gcry_strerror (rc)); return NULL; } + len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + buf = xtrymalloc (len); + if (!buf) + { + gcry_sexp_release (s_skey); + return NULL; + } + len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, buf, len); + assert (len); + gcry_sexp_release (s_skey); - rc = unprotect (s_skey); + rc = unprotect (&buf); if (rc) { - gcry_sexp_release (s_skey); log_error ("failed to unprotect the secret key: %s\n", gcry_strerror (rc)); + xfree (buf); + return NULL; + } + + /* arggg FIXME: does scna support secure memory? */ + rc = gcry_sexp_sscan (&s_skey, &erroff, + buf, gcry_sexp_canon_len (buf, 0, NULL, NULL)); + xfree (buf); + if (rc) + { + log_error ("failed to build S-Exp (off=%u): %s\n", + (unsigned int)erroff, gcry_strerror (rc)); return NULL; } diff --git a/agent/genkey.c b/agent/genkey.c index 166f4605a..2119bbb44 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -30,8 +30,9 @@ #include "agent.h" + static int -store_key (GCRY_SEXP private) +store_key (GCRY_SEXP private, const char *passphrase) { int i; char *fname; @@ -58,9 +59,11 @@ store_key (GCRY_SEXP private) xfree (fname); return seterr (General_Error); } - fp = fopen (fname, "wbx"); - if (!fp) - { + fp = fopen (fname, "wbx"); /* FIXME: the x is a GNU extension - let + configure check whether this actually + works */ + if (!fp) + { log_error ("can't create `%s': %s\n", fname, strerror (errno)); xfree (fname); return seterr (File_Create_Error); @@ -79,6 +82,24 @@ store_key (GCRY_SEXP private) len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, buf, len); assert (len); + if (passphrase) + { + unsigned char *p; + int rc; + + rc = agent_protect (buf, passphrase, &p, &len); + if (rc) + { + fclose (fp); + remove (fname); + xfree (fname); + xfree (buf); + return rc; + } + xfree (buf); + buf = p; + } + if (fwrite (buf, len, 1, fp) != 1) { log_error ("error writing `%s': %s\n", fname, strerror (errno)); @@ -111,6 +132,7 @@ agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen, FILE *outfp) { GCRY_SEXP s_keyparam, s_key, s_private, s_public; + struct pin_entry_info_s *pi, *pi2; int rc; size_t len; char *buf; @@ -122,13 +144,48 @@ agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen, return seterr (Invalid_Data); } - /* fixme: Get the passphrase now, cause key generation may take a while */ + /* Get the passphrase now, cause key generation may take a while */ + { + const char *text1 = trans ("Please enter the passphrase to%0A" + "to protect your new key"); + const char *text2 = trans ("Please re-enter this passphrase"); + const char *nomatch = trans ("does not match - try again"); + int tries = 0; + + pi = gcry_calloc_secure (2, sizeof (*pi) + 100); + pi2 = pi + sizeof *pi; + pi->max_length = 100; + pi->max_tries = 3; + pi2->max_length = 100; + pi2->max_tries = 3; + + rc = agent_askpin (text1, NULL, pi); + if (!rc) + { + do + { + rc = agent_askpin (text2, tries? nomatch:NULL, pi2); + tries++; + } + while (!rc && tries < 3 && strcmp (pi->pin, pi2->pin)); + if (!rc && strcmp (pi->pin, pi2->pin)) + rc = GNUPG_Canceled; + } + if (rc) + return rc; + if (!*pi->pin) + { + xfree (pi); + pi = NULL; /* use does not want a passphrase */ + } + } rc = gcry_pk_genkey (&s_key, s_keyparam ); gcry_sexp_release (s_keyparam); if (rc) { log_error ("key generation failed: %s\n", gcry_strerror (rc)); + xfree (pi); return map_gcry_err (rc); } @@ -138,6 +195,7 @@ agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen, { log_error ("key generation failed: invalid return value\n"); gcry_sexp_release (s_key); + xfree (pi); return seterr (Invalid_Data); } s_public = gcry_sexp_find_token (s_key, "public-key", 0); @@ -146,13 +204,15 @@ agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen, log_error ("key generation failed: invalid return value\n"); gcry_sexp_release (s_private); gcry_sexp_release (s_key); + xfree (pi); return seterr (Invalid_Data); } gcry_sexp_release (s_key); s_key = NULL; /* store the secret key */ log_debug ("storing private key\n"); - rc = store_key (s_private); + rc = store_key (s_private, pi->pin); + xfree (pi); pi = NULL; gcry_sexp_release (s_private); if (rc) { diff --git a/agent/keyformat.txt b/agent/keyformat.txt index 4f81f5b1d..ab2ad65fd 100644 --- a/agent/keyformat.txt +++ b/agent/keyformat.txt @@ -6,7 +6,7 @@ Some notes on the format of the secret keys used with gpg-agent. The secret keys[1] are stored on a per file basis in a directory below -the .gnupg home directory. This directory is named +the ~/.gnupg home directory. This directory is named private-keys-v1.d @@ -26,19 +26,15 @@ example of an unprotected file: (q #00f7a7c..[some bytes not shown]..61#) (u #304559a..[some bytes not shown]..9b#) ) + (uri http://foo.bar x-foo:whatever_you_want) ) Actually this form should not be used for regular purposes and only accepted by gpg-agent with the configuration option: ---allow-non-canonical-key-format. +--allow-non-canonical-key-format. The regular way to represent the +keys is in canonical representation[3]: -The regular way to represent the keys is in canonical representation -with the additional requirement of an extra object container around -it[3]: - -(oid.1.3.6.1.4.1.11591.2.2.2 - (keyinfo human_readable_information_to_decribe_this_key) - (private-key +(private-key (rsa (n #00e0ce9..[some bytes not shown]..51#) (e #010001#) @@ -47,76 +43,79 @@ it[3]: (q #00f7a7c..[some bytes not shown]..61#) (u #304559a..[some bytes not shown]..9b#) ) - ) -) + (uri http://foo.bar x-foo:whatever_you_want) +) + This describes an unprotected key; a protected key is like this: -(oid.1.3.6.1.4.1.11591.2.2.3 - (keyinfo human_readable_information_to_decribe_this_key) - (private-key +(protected-private-key (rsa (n #00e0ce9..[some bytes not shown]..51#) (e #010001#) - (oid.1.3.6.1.4.1.11591.2.1.1.1 (parms) encrypted_octet_string) + (protected mode (parms) encrypted_octet_string) ) - ) -) + (uri http://foo.bar x-foo:whatever_you_want) +) + In this scheme the encrypted_octet_string is encrypted according to -the scheme identifier by the OID, most protection algorithms need -some parameters, which are given in a list before the +the algorithm described after the keyword protected; most protection +algorithms need some parameters, which are given in a list before the encrypted_octet_string. The result of the decryption process is a list of the secret key parameters. -Defined protection methods are: +The only available protection mode for now is -1.3.6.1.4.1.gnu(11591).aegypten(2) -.algorithms(1).keyprotection(1).s2k3-sha1-aes-cbc(1) + openpgp-s2k3-sha1-aes-cbc -This uses AES in CBC mode for encryption, SHA-1 for integrity -protection and the String to Key algorithm 3 from OpenPGP (rfc2440). +which describesan algorithm using using AES in CBC mode for +encryption, SHA-1 for integrity protection and the String to Key +algorithm 3 from OpenPGP (rfc2440). Example: -(oid.1.3.6.1.4.1.11591.2.1.1.1 - ((salt iterations) iv) +(protected openpgp-s2k3-sha1-aes-cbc + ((sha1 16byte_salt no_of_iterations) 16byte_iv) encrypted_octet_string ) The encrypted_octet string should yield this S-Exp (in canonical representation) after decryption: -(sha1_hash - (d #046129F..[some bytes not shown]..81#) - (p #00e861b..[some bytes not shown]..f1#) - (q #00f7a7c..[some bytes not shown]..61#) - (u #304559a..[some bytes not shown]..9b#) +( + ( + (d #046129F..[some bytes not shown]..81#) + (p #00e861b..[some bytes not shown]..f1#) + (q #00f7a7c..[some bytes not shown]..61#) + (u #304559a..[some bytes not shown]..9b#) + ) + (hash sha1 #...[hashvalue]...#) ) For padding reasons, random bytes are appended to this list - they can easily be stripped by looking for the end of the list. -The first element is the SHA-1 hash calculated on the concatenation of the -public key and secret key parameter lists: i.e one has to hash the -concatenatiohn of these 6 canonical encoded lists for RSA, including -the parenthesis. +The hash is calculated on the concatenation of the public key and +secret key parameter lists: i.e it is required to hash the +concatenation of these 6 canonical encoded lists for RSA, including +the parenthesis and the algorithm keyword. +(rsa (n #00e0ce9..[some bytes not shown]..51#) (e #010001#) (d #046129F..[some bytes not shown]..81#) (p #00e861b..[some bytes not shown]..f1#) (q #00f7a7c..[some bytes not shown]..61#) (u #304559a..[some bytes not shown]..9b#) - +) After decryption the hash must be recalculated and compared against the stored one - If they don't match the integrity of the key is not given. -TODO: write a more elaborated version. diff --git a/agent/protect-tool.c b/agent/protect-tool.c new file mode 100644 index 000000000..df58290ed --- /dev/null +++ b/agent/protect-tool.c @@ -0,0 +1,355 @@ +/* protect-tool.c - A tool to text the secret key protection + * Copyright (C) 2002 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 <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <gcrypt.h> + +#define JNLIB_NEED_LOG_LOGV +#include "agent.h" + +#define N_(a) a +#define _(a) a + + +enum cmd_and_opt_values +{ aNull = 0, + oVerbose = 'v', + oArmor = 'a', + oPassphrase = 'P', + + oProtect = 'p', + oUnprotect = 'u', + + oNoVerbose = 500, + +aTest }; + + +static int opt_armor; +static const char *passphrase = "abc"; + +static ARGPARSE_OPTS opts[] = { + + { 301, NULL, 0, N_("@Options:\n ") }, + + { oVerbose, "verbose", 0, "verbose" }, + { oArmor, "armor", 0, "write output in advanced format" }, + { oPassphrase, "passphrase", 2, "|STRING| Use passphrase STRING" }, + { oProtect, "protect", 256, "protect a private key"}, + { oUnprotect, "unprotect", 256, "unprotect a private key"}, + + {0} +}; + +static const char * +my_strusage (int level) +{ + const char *p; + switch (level) + { + case 11: p = "protect-tool (GnuPG)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); + break; + case 1: + case 40: p = _("Usage: protect-tool [options] (-h for help)\n"); + break; + case 41: p = _("Syntax: protect-tool [options] [args]]\n" + "INTERNAL USE ONLY!\n"); + break; + + default: p = NULL; + } + return p; +} + + + +static void +i18n_init (void) +{ +#ifdef USE_SIMPLE_GETTEXT + set_gettext_file( PACKAGE ); +#else +#ifdef ENABLE_NLS + /* gtk_set_locale (); HMMM: We have not yet called gtk_init */ + bindtextdomain( PACKAGE, GNUPG_LOCALEDIR ); + textdomain( PACKAGE ); +#endif +#endif +} + + + +/* Used by gcry for logging */ +static void +my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr) +{ + /* translate the log levels */ + switch (level) + { + case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break; + case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break; + case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break; + case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break; + case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break; + case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break; + case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break; + default: level = JNLIB_LOG_ERROR; break; + } + log_logv (level, fmt, arg_ptr); +} + + +static unsigned char * +make_canonical (const char *fname, const char *buf, size_t buflen) +{ + int rc; + size_t erroff, len; + GCRY_SEXP sexp; + unsigned char *result; + + rc = gcry_sexp_sscan (&sexp, &erroff, buf, buflen); + if (rc) + { + log_error ("invalid S-Expression in `%s' (off=%u): %s\n", + fname, (unsigned int)erroff, gcry_strerror (rc)); + return NULL; + } + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + result = xmalloc (len); + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, result, len); + assert (len); + gcry_sexp_release (sexp); + return result; +} + +static char * +make_advanced (const unsigned char *buf, size_t buflen) +{ + int rc; + size_t erroff, len; + GCRY_SEXP sexp; + unsigned char *result; + + rc = gcry_sexp_sscan (&sexp, &erroff, buf, buflen); + if (rc) + { + log_error ("invalid canonical S-Expression (off=%u): %s\n", + (unsigned int)erroff, gcry_strerror (rc)); + return NULL; + } + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); + assert (len); + result = xmalloc (len); + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len); + assert (len); + gcry_sexp_release (sexp); + return result; +} + + +static unsigned char * +read_key (const char *fname) +{ + FILE *fp; + struct stat st; + char *buf; + size_t buflen; + unsigned char *key; + + fp = fopen (fname, "rb"); + if (!fp) + { + log_error ("can't open `%s': %s\n", fname, strerror (errno)); + return NULL; + } + + if (fstat (fileno(fp), &st)) + { + log_error ("can't stat `%s': %s\n", fname, strerror (errno)); + fclose (fp); + return NULL; + } + + buflen = st.st_size; + buf = xmalloc (buflen+1); + if (fread (buf, buflen, 1, fp) != 1) + { + log_error ("error reading `%s': %s\n", fname, strerror (errno)); + fclose (fp); + xfree (buf); + return NULL; + } + fclose (fp); + + key = make_canonical (fname, buf, buflen); + xfree (buf); + return key; +} + + + +static void +read_and_protect (const char *fname) +{ + int rc; + unsigned char *key; + unsigned char *result; + size_t resultlen; + + key = read_key (fname); + if (!key) + return; + + rc = agent_protect (key, passphrase, &result, &resultlen); + xfree (key); + if (rc) + { + log_error ("protecting the key failed: %s\n", gnupg_strerror (rc)); + return; + } + + if (opt_armor) + { + char *p = make_advanced (result, resultlen); + xfree (result); + if (!p) + return; + result = p; + resultlen = strlen (p); + } + + fwrite (result, resultlen, 1, stdout); + xfree (result); +} + + +static void +read_and_unprotect (const char *fname) +{ + int rc; + unsigned char *key; + unsigned char *result; + size_t resultlen; + + key = read_key (fname); + if (!key) + return; + + rc = agent_unprotect (key, passphrase, &result, &resultlen); + xfree (key); + if (rc) + { + log_error ("unprotecting the key failed: %s\n", gnupg_strerror (rc)); + return; + } + + if (opt_armor) + { + char *p = make_advanced (result, resultlen); + xfree (result); + if (!p) + return; + result = p; + resultlen = strlen (p); + } + + fwrite (result, resultlen, 1, stdout); + xfree (result); +} + + + +int +main (int argc, char **argv ) +{ + ARGPARSE_ARGS pargs; + int cmd = 0; + + set_strusage (my_strusage); + gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); + log_set_prefix ("protect-tool", 1); + i18n_init (); + + if (!gcry_check_version ( "1.1.5" ) ) + { + log_fatal( _("libgcrypt is too old (need %s, have %s)\n"), + "1.1.5", gcry_check_version (NULL) ); + } + + gcry_set_log_handler (my_gcry_logger, NULL); + + gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); + + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1; /* do not remove the args */ + while (arg_parse (&pargs, opts) ) + { + switch (pargs.r_opt) + { + case oVerbose: opt.verbose++; break; + case oArmor: opt_armor=1; break; + + case oProtect: cmd = oProtect; break; + case oUnprotect: cmd = oUnprotect; break; + + case oPassphrase: passphrase = pargs.r.ret_str; break; + + default : pargs.err = 2; break; + } + } + if (log_get_errorcount(0)) + exit(2); + + if (argc != 1) + usage (1); + + if (cmd == oProtect) + read_and_protect (*argv); + else if (cmd == oUnprotect) + read_and_unprotect (*argv); + else + log_info ("no action requested\n"); + + return 0; +} + +void +agent_exit (int rc) +{ + rc = rc? rc : log_get_errorcount(0)? 2 : 0; + exit (rc); +} diff --git a/agent/protect.c b/agent/protect.c new file mode 100644 index 000000000..6b95dabfa --- /dev/null +++ b/agent/protect.c @@ -0,0 +1,861 @@ +/* protect.c - Un/Protect a secret key + * Copyright (C) 1998, 1999, 2000, 2001, 2002 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 <config.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "agent.h" + +#define PROT_CIPHER GCRY_CIPHER_AES +#define PROT_CIPHER_STRING "aes" +#define PROT_CIPHER_KEYLEN (128/8) + + +/* A table containing the information needed to create a protected + private key */ +static struct { + const char *algo; + const char *parmlist; + int prot_from, prot_to; +} protect_info[] = { + { "rsa", "nedpqu", 2, 5 }, + { NULL } +}; + + +static int +hash_passphrase (const char *passphrase, int hashalgo, + int s2kmode, + const unsigned char *s2ksalt, unsigned long s2kcount, + unsigned char *key, size_t keylen); + + + +/* Return the length of the next S-Exp part and update the pointer to + the first data byte. 0 is return on error */ +static size_t +snext (unsigned char const **buf) +{ + const unsigned char *s; + int n; + + s = *buf; + for (n=0; *s && *s != ':' && digitp (s); s++) + n = n*10 + atoi_1 (s); + if (!n || *s != ':') + return 0; /* we don't allow empty lengths */ + *buf = s+1; + return n; +} + +/* Skip over the S-Expression BUF points to and update BUF to point to + the chacter right behind. DEPTH gives the initial number of open + lists and may be passed as a positive number to skip over the + remainder of an S-Expression if the current position is somewhere + in an S-Expression. The function may return an error code if it + encounters an impossible conditions */ +static int +sskip (unsigned char const **buf, int *depth) +{ + const unsigned char *s = *buf; + size_t n; + int d = *depth; + + while (d > 0) + { + if (*s == '(') + { + d++; + s++; + } + else if (*s == ')') + { + d--; + s++; + } + else + { + if (!d) + return GNUPG_Invalid_Sexp; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + s += n; + } + } + *buf = s; + *depth = d; + return 0; +} + + +/* Check whether the the string at the address BUF points to matches + the token. Return true on match and update BUF to point behind the + token. */ +static int +smatch (unsigned char const **buf, size_t buflen, const char *token) +{ + size_t toklen = strlen (token); + + if (buflen != toklen || memcmp (*buf, token, toklen)) + return 0; + *buf += toklen; + return 1; +} + + + +/* Calculate the MIC for a private key S-Exp. SHA1HASH should pint to + a 20 byte buffer. This function is suitable for any algorithms. */ +static int +calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash) +{ + const unsigned char *hash_begin, *hash_end; + const unsigned char *s; + size_t n; + + s = plainkey; + if (*s != '(') + return GNUPG_Invalid_Sexp; + s++; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + if (!smatch (&s, n, "private-key")) + return GNUPG_Unknown_Sexp; + if (*s != '(') + return GNUPG_Unknown_Sexp; + hash_begin = s; + s++; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + s += n; /* skip over the algorithm name */ + + while (*s == '(') + { + s++; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + s += n; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + s += n; + if ( *s != ')' ) + return GNUPG_Invalid_Sexp; + s++; + } + if (*s != ')') + return GNUPG_Invalid_Sexp; + s++; + hash_end = s; + + gcry_md_hash_buffer (GCRY_MD_SHA1, sha1hash, + hash_begin, hash_end - hash_begin); + + return 0; +} + + + +/* Encrypt the parameter block starting at PROTBEGIN with length + PROTLEN using the utf8 encoded key PASSPHRASE and return the entire + encrypted block in RESULT or ereturn with an error code. SHA1HASH + is the 20 byte SHA-1 hash required for the integrity code. + + The parameter block is expected to be an incomplete S-Expression of + the form (example in advanced format): + + (d #046129F..[some bytes not shown]..81#) + (p #00e861b..[some bytes not shown]..f1#) + (q #00f7a7c..[some bytes not shown]..61#) + (u #304559a..[some bytes not shown]..9b#) + + the returned block is the S-Expression: + + (protected mode (parms) encrypted_octet_string) + +*/ +static int +do_encryption (const char *protbegin, size_t protlen, + const char *passphrase, const unsigned char *sha1hash, + unsigned char **result, size_t *resultlen) +{ + GCRY_CIPHER_HD hd; + const char *modestr = "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc"; + int blklen, enclen, outlen; + char *iv = NULL; + int rc = 0; + char *outbuf = NULL; + char *p; + int saltpos, ivpos, encpos; + + hd = gcry_cipher_open (PROT_CIPHER, GCRY_CIPHER_MODE_CBC, + GCRY_CIPHER_SECURE); + if (!hd) + return map_gcry_err (gcry_errno()); + + + /* We need to work on a copy of the data because this makes it + easier to add the trailer and the padding and more important we + have to prefix the text with 2 parenthesis, so we have to + allocate enough space for: + + ((<parameter_list>)(4:hash4:sha120:<hashvalue>)) + padding + + We always append a full block of random bytes as padding but + encrypt only what is needed for a full blocksize */ + blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER); + outlen = 2 + protlen + 2 + 6 + 6 + 23 + 2 + blklen; + enclen = outlen/blklen * blklen; + outbuf = gcry_malloc_secure (outlen); + if (!outbuf) + rc = GNUPG_Out_Of_Core; + if (!rc) + { + /* allocate random bytes to be used as IV, padding and s2k salt*/ + iv = gcry_random_bytes (blklen*2+8, GCRY_WEAK_RANDOM); + if (!iv) + rc = GNUPG_Out_Of_Core; + else + rc = gcry_cipher_setiv (hd, iv, blklen); + } + if (!rc) + { + unsigned char *key; + size_t keylen = PROT_CIPHER_KEYLEN; + + key = gcry_malloc_secure (keylen); + if (!key) + rc = GNUPG_Out_Of_Core; + else + { + rc = hash_passphrase (passphrase, GCRY_MD_SHA1, + 3, iv+2*blklen, 96, key, keylen); + if (!rc) + rc = gcry_cipher_setkey (hd, key, keylen); + xfree (key); + } + } + if (!rc) + { + p = outbuf; + *p++ = '('; + *p++ = '('; + memcpy (p, protbegin, protlen); + p += protlen; + memcpy (p, ")(4:hash4:sha120:", 17); + p += 17; + memcpy (p, sha1hash, 20); + p += 20; + *p++ = ')'; + *p++ = ')'; + memcpy (p, iv+blklen, blklen); + p += blklen; + assert ( p - outbuf == outlen); + rc = gcry_cipher_encrypt (hd, outbuf, enclen, NULL, 0); + } + gcry_cipher_close (hd); + if (rc) + { + xfree (iv); + xfree (outbuf); + return rc; + } + + /* Now allocate the buffer we want to return. This is + + (protected openpgp-s2k3-sha1-aes-cbc + ((sha1 salt no_of_iterations) 16byte_iv) + encrypted_octet_string) + + in canoncical format of course. We use asprintf and %n modifier + and spaces as palceholders. */ + asprintf (&p, + "(9:protected%d:%s((4:sha18:%n_8bytes_2:96)%d:%n%*s)%d:%n%*s)", + (int)strlen (modestr), modestr, + &saltpos, + blklen, &ivpos, blklen, "", + enclen, &encpos, enclen, ""); + if (p) + { /* asprintf does not use out malloc system */ + char *psave = p; + p = xtrymalloc (strlen (psave)+1); + if (p) + strcpy (p, psave); + free (psave); + } + if (!p) + { + xfree (iv); + xfree (outbuf); + return GNUPG_Out_Of_Core; + } + *resultlen = strlen (p); + *result = p; + memcpy (p+saltpos, iv+2*blklen, 8); + memcpy (p+ivpos, iv, blklen); + memcpy (p+encpos, outbuf, enclen); + xfree (iv); + xfree (outbuf); + return 0; +} + + + +/* Protect the key encoded in canonical format in plainkey. We assume + a valid S-Exp here. */ +int +agent_protect (const unsigned char *plainkey, const char *passphrase, + unsigned char **result, size_t *resultlen) +{ + int rc; + const unsigned char *s; + const unsigned char *hash_begin, *hash_end; + const unsigned char *prot_begin, *prot_end, *real_end; + size_t n; + int c, infidx, i; + unsigned char hashvalue[20]; + unsigned char *protected; + size_t protectedlen; + int depth = 0; + unsigned char *p; + + s = plainkey; + if (*s != '(') + return GNUPG_Invalid_Sexp; + depth++; + s++; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + if (!smatch (&s, n, "private-key")) + return GNUPG_Unknown_Sexp; + if (*s != '(') + return GNUPG_Unknown_Sexp; + depth++; + hash_begin = s; + s++; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + + for (infidx=0; protect_info[infidx].algo + && !smatch (&s, n, protect_info[infidx].algo); infidx++) + ; + if (!protect_info[infidx].algo) + return GNUPG_Unsupported_Algorithm; + + prot_begin = prot_end = NULL; + for (i=0; (c=protect_info[infidx].parmlist[i]); i++) + { + if (i == protect_info[infidx].prot_from) + prot_begin = s; + if (*s != '(') + return GNUPG_Invalid_Sexp; + depth++; + s++; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + if (n != 1 || c != *s) + return GNUPG_Invalid_Sexp; + s += n; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + s +=n; /* skip value */ + if (*s != ')') + return GNUPG_Invalid_Sexp; + depth--; + if (i == protect_info[infidx].prot_to) + prot_end = s; + s++; + } + if (*s != ')' || !prot_begin || !prot_end ) + return GNUPG_Invalid_Sexp; + depth--; + hash_end = s; + s++; + /* skip to the end of the S-exp */ + assert (depth == 1); + rc = sskip (&s, &depth); + if (rc) + return rc; + assert (!depth); + real_end = s-1; + + gcry_md_hash_buffer (GCRY_MD_SHA1, hashvalue, + hash_begin, hash_end - hash_begin + 1); + + rc = do_encryption (prot_begin, prot_end - prot_begin + 1, + passphrase, hashvalue, + &protected, &protectedlen); + if (rc) + return rc; + + /* Now create the protected version of the key. Note that the 10 + extra bytes are for for the inserted "protected-" string (the + beginning of the plaintext reads: "((11:private-key(" ). */ + *resultlen = (10 + + (prot_begin-plainkey) + + protectedlen + + (real_end-prot_end)); + *result = p = xtrymalloc (*resultlen); + if (!p) + { + xfree (protected); + return GNUPG_Out_Of_Core; + } + memcpy (p, "(21:protected-", 14); + p += 14; + memcpy (p, plainkey+4, prot_begin - plainkey - 4); + p += prot_begin - plainkey - 4; + memcpy (p, protected, protectedlen); + p += protectedlen; + memcpy (p, prot_end+1, real_end - prot_end); + p += real_end - prot_end; + assert ( p - *result == *resultlen); + xfree (protected); + return 0; +} + + +/* Do the actual decryption and check the return list for consistency. */ +static int +do_decryption (const unsigned char *protected, size_t protectedlen, + const char *passphrase, + const unsigned char *s2ksalt, unsigned long s2kcount, + const unsigned char *iv, size_t ivlen, + unsigned char **result) +{ + int rc = 0; + int blklen; + GCRY_CIPHER_HD hd; + unsigned char *outbuf; + size_t reallen; + + blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER); + if (protectedlen < 4 || (protectedlen%blklen)) + return GNUPG_Corrupted_Protection; + + hd = gcry_cipher_open (PROT_CIPHER, GCRY_CIPHER_MODE_CBC, + GCRY_CIPHER_SECURE); + if (!hd) + return map_gcry_err (gcry_errno()); + + outbuf = gcry_malloc_secure (protectedlen); + if (!outbuf) + rc = GNUPG_Out_Of_Core; + if (!rc) + rc = gcry_cipher_setiv (hd, iv, ivlen); + if (!rc) + { + unsigned char *key; + size_t keylen = PROT_CIPHER_KEYLEN; + + key = gcry_malloc_secure (keylen); + if (!key) + rc = GNUPG_Out_Of_Core; + else + { + rc = hash_passphrase (passphrase, GCRY_MD_SHA1, + 3, s2ksalt, s2kcount, key, keylen); + if (!rc) + rc = gcry_cipher_setkey (hd, key, keylen); + xfree (key); + } + } + if (!rc) + rc = gcry_cipher_decrypt (hd, outbuf, protectedlen, + protected, protectedlen); + gcry_cipher_close (hd); + if (rc) + { + xfree (outbuf); + return rc; + } + /* do a quick check first */ + if (*outbuf != '(' && outbuf[1] != '(') + { + xfree (outbuf); + return GNUPG_Bad_Passphrase; + } + /* check that we have a consistent S-Exp */ + reallen = gcry_sexp_canon_len (outbuf, protectedlen, NULL, NULL); + if (!reallen || (reallen + blklen < protectedlen) ) + { + xfree (outbuf); + return GNUPG_Bad_Passphrase; + } + *result = outbuf; + return 0; +} + + +/* Merge the parameter list contained in CLEARTEXT with the original + protect lists PROTECTEDKEY by replacing the list at REPLACEPOS. + Return the new list in RESULT and the MIC value in the 20 byte + buffer SHA1HASH. */ +static int +merge_lists (const unsigned char *protectedkey, + size_t replacepos, + const unsigned char *cleartext, + unsigned char *sha1hash, unsigned char **result) +{ + size_t n, newlistlen; + unsigned char *newlist, *p; + const unsigned char *s; + const unsigned char *startpos, *endpos; + int i, rc; + + if (replacepos < 26) + return GNUPG_Bug; + + /* Estimate the required size of the resulting list. We have a large + safety margin of >20 bytes (MIC hash from CLEARTEXT and the + removed "protected-" */ + newlistlen = gcry_sexp_canon_len (protectedkey, 0, NULL, NULL); + if (!newlistlen) + return GNUPG_Bug; + n = gcry_sexp_canon_len (cleartext, 0, NULL, NULL); + if (!n) + return GNUPG_Bug; + newlistlen += n; + newlist = gcry_malloc_secure (newlistlen); + if (!newlist) + return GNUPG_Out_Of_Core; + + /* Copy the initial segment */ + strcpy (newlist, "(11:private-key"); + p = newlist + 15; + memcpy (p, protectedkey+15+10, replacepos-15-10); + p += replacepos-15-10; + + /* copy the cleartext */ + s = cleartext; + if (*s != '(' && s[1] != '(') + return GNUPG_Bug; /*we already checked this */ + s += 2; + startpos = s; + while ( *s == '(' ) + { + s++; + n = snext (&s); + if (!n) + goto invalid_sexp; + s += n; + n = snext (&s); + if (!n) + goto invalid_sexp; + s += n; + if ( *s != ')' ) + goto invalid_sexp; + s++; + } + if ( *s != ')' ) + goto invalid_sexp; + endpos = s; + s++; + /* short intermezzo: Get the MIC */ + if (*s != '(') + goto invalid_sexp; + s++; + n = snext (&s); + if (!smatch (&s, n, "hash")) + goto invalid_sexp; + n = snext (&s); + if (!smatch (&s, n, "sha1")) + goto invalid_sexp; + n = snext (&s); + if (n != 20) + goto invalid_sexp; + memcpy (sha1hash, s, 20); + s += n; + if (*s != ')') + goto invalid_sexp; + /* end intermezzo */ + + /* append the parameter list */ + memcpy (p, startpos, endpos - startpos); + p += endpos - startpos; + + /* skip overt the protected list element in the original list */ + s = protectedkey + replacepos; + assert (*s == '('); + s++; + i = 1; + rc = sskip (&s, &i); + if (rc) + goto failure; + startpos = s; + i = 2; /* we are inside this level */ + rc = sskip (&s, &i); + if (rc) + goto failure; + assert (s[-1] == ')'); + endpos = s; /* one behind the end of the list */ + + /* append the rest */ + memcpy (p, startpos, endpos - startpos); + p += endpos - startpos; + + /* ready */ + *result = newlist; + return 0; + + failure: + xfree (newlist); + return rc; + + invalid_sexp: + xfree (newlist); + return GNUPG_Invalid_Sexp; +} + + + +/* Unprotect the key encoded in canonical format. We assume a valid + S-Exp here. */ +int +agent_unprotect (const unsigned char *protectedkey, const char *passphrase, + unsigned char **result, size_t *resultlen) +{ + int rc; + const unsigned char *s; + size_t n; + int infidx, i; + unsigned char sha1hash[20], sha1hash2[20]; + const unsigned char *s2ksalt; + unsigned long s2kcount; + const unsigned char *iv; + const unsigned char *prot_begin; + unsigned char *cleartext; + unsigned char *final; + + s = protectedkey; + if (*s != '(') + return GNUPG_Invalid_Sexp; + s++; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + if (!smatch (&s, n, "protected-private-key")) + return GNUPG_Unknown_Sexp; + if (*s != '(') + return GNUPG_Unknown_Sexp; + s++; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + + for (infidx=0; protect_info[infidx].algo + && !smatch (&s, n, protect_info[infidx].algo); infidx++) + ; + if (!protect_info[infidx].algo) + return GNUPG_Unsupported_Algorithm; + + /* now find the list with the protected information. Here is an + example for such a list: + (protected openpgp-s2k3-sha1-aes-cbc + ((sha1 <salt> <count>) <Initialization_Vector>) + <encrypted_data>) + */ + for (;;) + { + if (*s != '(') + return GNUPG_Invalid_Sexp; + prot_begin = s; + s++; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + if (smatch (&s, n, "protected")) + break; + s += n; + i = 1; + rc = sskip (&s, &i); + if (rc) + return rc; + } + /* found */ + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + if (!smatch (&s, n, "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc")) + return GNUPG_Unsupported_Protection; + if (*s != '(' || s[1] != '(') + return GNUPG_Invalid_Sexp; + s += 2; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + if (!smatch (&s, n, "sha1")) + return GNUPG_Unsupported_Protection; + n = snext (&s); + if (n != 8) + return GNUPG_Corrupted_Protection; + s2ksalt = s; + s += n; + n = snext (&s); + if (!n) + return GNUPG_Corrupted_Protection; + /* We expect a list close as next, so we can simply use strtoul() + here. We might want to check that we only have digits - but this + is nothing we should worry about */ + if (s[n] != ')' ) + return GNUPG_Invalid_Sexp; + s2kcount = strtoul (s, NULL, 10); + if (!s2kcount) + return GNUPG_Corrupted_Protection; + s += n; + s++; /* skip list end */ + + n = snext (&s); + if (n != 16) /* Wrong blocksize for IV (we support ony aes-128) */ + return GNUPG_Corrupted_Protection; + iv = s; + s += n; + if (*s != ')' ) + return GNUPG_Invalid_Sexp; + s++; + n = snext (&s); + if (!n) + return GNUPG_Invalid_Sexp; + + rc = do_decryption (s, n, + passphrase, s2ksalt, s2kcount, + iv, 16, + &cleartext); + if (rc) + return rc; + + rc = merge_lists (protectedkey, prot_begin-protectedkey, cleartext, + sha1hash, &final); + xfree (cleartext); + if (rc) + return rc; + + rc = calculate_mic (final, sha1hash2); + if (!rc && memcmp (sha1hash, sha1hash2, 20)) + rc = GNUPG_Corrupted_Protection; + if (rc) + { + xfree (final); + return rc; + } + + *result = final; + *resultlen = gcry_sexp_canon_len (final, 0, NULL, NULL); + return 0; +} + + + + +/* Transform a passphrase into a suitable key of length KEYLEN and + store this key in the caller provided buffer KEY. The caller must + provide an HASHALGO, a valid S2KMODE (see rfc-2440) and depending on + that mode an S2KSALT of 8 random bytes and an S2KCOUNT (a suitable + value is 96). + + Returns an error code on failure. */ +static int +hash_passphrase (const char *passphrase, int hashalgo, + int s2kmode, + const unsigned char *s2ksalt, + unsigned long s2kcount, + unsigned char *key, size_t keylen) +{ + GCRY_MD_HD md; + int pass, i; + int used = 0; + int pwlen = strlen (passphrase); + + if ( (s2kmode != 0 && s2kmode != 1 && s2kmode != 3) + || !hashalgo || !keylen || !key || !passphrase) + return GNUPG_Invalid_Value; + if ((s2kmode == 1 ||s2kmode == 3) && !s2ksalt) + return GNUPG_Invalid_Value; + + md = gcry_md_open (hashalgo, GCRY_MD_FLAG_SECURE); + if (!md) + return map_gcry_err (gcry_errno()); + + for (pass=0; used < keylen; pass++) + { + if (pass) + { + gcry_md_reset (md); + for (i=0; i < pass; i++) /* preset the hash context */ + gcry_md_putc (md, 0); + } + + if (s2kmode == 1 || s2kmode == 3) + { + int len2 = pwlen + 8; + unsigned long count = len2; + + if (s2kmode == 3) + { + count = (16ul + (s2kcount & 15)) << ((s2kcount >> 4) + 6); + if (count < len2) + count = len2; + } + + while (count > len2) + { + gcry_md_write (md, s2ksalt, 8); + gcry_md_write (md, passphrase, pwlen); + count -= len2; + } + if (count < 8) + gcry_md_write (md, s2ksalt, count); + else + { + gcry_md_write (md, s2ksalt, 8); + count -= 8; + gcry_md_write (md, passphrase, count); + } + } + else + gcry_md_write (md, passphrase, pwlen); + + gcry_md_final (md); + i = gcry_md_get_algo_dlen (hashalgo); + if (i > keylen - used) + i = keylen - used; + memcpy (key+used, gcry_md_read (md, hashalgo), i); + used += i; + } + gcry_md_close(md); + return 0; +} + + diff --git a/agent/query.c b/agent/query.c index fcee18c2a..3b8cd08df 100644 --- a/agent/query.c +++ b/agent/query.c @@ -137,13 +137,13 @@ all_digitsp( const char *s) number here and repeat it as long as we have invalid formed numbers. */ int -agent_askpin (const char *desc_text, +agent_askpin (const char *desc_text, const char *start_err_text, struct pin_entry_info_s *pininfo) { int rc; char line[ASSUAN_LINELENGTH]; struct entry_parm_s parm; - const char *errtext = NULL; + const char *errtext = start_err_text; if (opt.batch) return 0; /* fixme: we should return BAD PIN */ @@ -180,8 +180,14 @@ agent_askpin (const char *desc_text, if (errtext) { /* fixme: should we show the try count? It must be translated */ - snprintf (line, DIM(line)-1, "SETERROR %s (try %d of %d)", - errtext, pininfo->failed_tries+1, pininfo->max_tries); + if (start_err_text) + { + snprintf (line, DIM(line)-1, "SETERROR %s", errtext); + start_err_text = NULL; + } + else + snprintf (line, DIM(line)-1, "SETERROR %s (try %d of %d)", + errtext, pininfo->failed_tries+1, pininfo->max_tries); line[DIM(line)-1] = 0; rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL); if (rc) |