From 083010a53d213a1662afad1950c41fcb46939d7f Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 19 Mar 2007 18:54:34 +0000 Subject: [PATCH] * PKCS#12 import now tries several encodings in case the passphrase was not utf-8 encoded. --- NEWS | 3 + TODO | 4 -- agent/ChangeLog | 10 ++- agent/minip12.c | 179 +++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 167 insertions(+), 29 deletions(-) diff --git a/NEWS b/NEWS index d20a99b42..42facb376 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,9 @@ Noteworthy changes in version 2.0.4 * The Assuan key listing commands are now also working for systems without the funopen/fopencookie API. + * PKCS#12 import now tries several encodings in case the passphrase + was not utf-8 encoded. + Noteworthy changes in version 2.0.3 (2007-03-08) ------------------------------------------------ diff --git a/TODO b/TODO index 4a8a48218..06c818856 100644 --- a/TODO +++ b/TODO @@ -83,10 +83,6 @@ * sm/ ** check that we issue NO_SECKEY xxx if a -u key was not found We don't. The messages returned are also wrong (recipient vs. signer). -** cmd_export - Does only work on systems with funopen/fopencookie. Changing is - easy. - * jnlib/ ** provide jnlib_malloc and try to remove all jnlib_xmalloc. diff --git a/agent/ChangeLog b/agent/ChangeLog index cc1ae2d53..3afb61dc4 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,10 @@ +2007-03-19 Werner Koch + + * minip12.c: Include iconv.h. + (decrypt_block): New. + (parse_bag_encrypted_data, parse_bag_data): Use it here. + (bag_data_p, bag_decrypted_data_p): New helpers. + 2007-03-06 Werner Koch * gpg-agent.c (main) : Add entries for all ttl options. @@ -1749,7 +1756,8 @@ Fri Aug 18 14:27:14 CEST 2000 Werner Koch * Makefile.am: New. - Copyright 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright 2001, 2002, 2003, 2004, 2005, + 2007 Free Software Foundation, Inc. This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without diff --git a/agent/minip12.c b/agent/minip12.c index 25a38b9c8..d6029f7b0 100644 --- a/agent/minip12.c +++ b/agent/minip12.c @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef TEST #include @@ -41,6 +42,12 @@ #define DIM(v) (sizeof(v)/sizeof((v)[0])) #endif +#ifndef ICONV_CONST +#define ICONV_CONST +#endif + + + enum { UNIVERSAL = 0, @@ -483,6 +490,120 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen, } +/* Decrypt a block of data and try several encodings of the key. + CIPHERTEXT is the encrypted data of size LENGTH bytes; PLAINTEXT is + a buffer of the same size to receive the decryption result. SALT, + SALTLEN, ITER and PW are the information required for decryption + and CIPHER_ALGO is the algorithm id to use. CHECK_FNC is a + function called with the plaintext and used to check whether the + decryption succeeded; i.e. that a correct passphrase has been + given. That function shall return true if the decryption has likely + succeeded. */ +static void +decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length, + char *salt, size_t saltlen, + int iter, const char *pw, int cipher_algo, + int (*check_fnc) (const void *, size_t)) +{ + static const char const *charsets[] = { + "", /* No conversion - use the UTF-8 passphrase direct. */ + "ISO-8859-1", + "ISO-8859-15", + "ISO-8859-2", + "ISO-8859-3", + "ISO-8859-4", + "ISO-8859-5", + "ISO-8859-6", + "ISO-8859-7", + "ISO-8859-8", + "ISO-8859-9", + "KOI8-R", + NULL + }; + int charsetidx = 0; + char *convertedpw = NULL; /* Malloced and converted password or NULL. */ + size_t convertedpwsize = 0; /* Allocated length. */ + + for (charsetidx=0; charsets[charsetidx]; charsetidx++) + { + if (*charsets[charsetidx]) + { + iconv_t cd; + const char *inptr; + char *outptr; + size_t inbytes, outbytes; + + if (!convertedpw) + { + /* We assume one byte encodings. Thus we can allocate + the buffer of the same size as the original + passphrase; the result will actually be shorter + then. */ + convertedpwsize = strlen (pw) + 1; + convertedpw = gcry_malloc_secure (convertedpwsize); + if (!convertedpw) + { + log_info ("out of secure memory while" + " converting passphrase\n"); + break; /* Give up. */ + } + } + + cd = iconv_open (charsets[charsetidx], "utf-8"); + if (cd == (iconv_t)(-1)) + continue; + + inptr = pw; + inbytes = strlen (pw); + outptr = convertedpw; + outbytes = convertedpwsize - 1; + if ( iconv (cd, (ICONV_CONST char **)&inptr, &inbytes, + &outptr, &outbytes) == (size_t)-1) + { + iconv_close (cd); + continue; + } + *outptr = 0; + iconv_close (cd); + log_info ("decryption failed; trying charset `%s'\n", + charsets[charsetidx]); + } + memcpy (plaintext, ciphertext, length); + crypt_block (plaintext, length, salt, saltlen, iter, + convertedpw? convertedpw:pw, cipher_algo, 0); + if (check_fnc (plaintext, length)) + break; /* Decryption succeeded. */ + } + gcry_free (convertedpw); +} + + +/* Return true if the decryption of an bag_encrypted_data object has + likely succeeded. */ +static int +bag_decrypted_data_p (const void *plaintext, size_t length) +{ + struct tag_info ti; + const unsigned char *p = plaintext; + size_t n = length; + + /* { */ + /* # warning debug code is enabled */ + /* FILE *fp = fopen ("tmp-rc2-plain.der", "wb"); */ + /* if (!fp || fwrite (p, n, 1, fp) != 1) */ + /* exit (2); */ + /* fclose (fp); */ + /* } */ + + if (parse_tag (&p, &n, &ti)) + return 0; + if (ti.class || ti.tag != TAG_SEQUENCE) + return 0; + if (parse_tag (&p, &n, &ti)) + return 0; + + return 1; +} /* Note: If R_RESULT is passed as NULL, a key object as already be processed and thus we need to skip it here. */ @@ -624,23 +745,13 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, log_error ("error allocating decryption buffer\n"); goto bailout; } - memcpy (plain, p, ti.length); - crypt_block (plain, ti.length, salt, saltlen, - iter, pw, - is_3des? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40, - 0); + decrypt_block (p, plain, ti.length, salt, saltlen, iter, pw, + is_3des? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40, + bag_decrypted_data_p); n = ti.length; startoffset = 0; p_start = p = plain; -/* { */ -/* # warning debug code is enabled */ -/* FILE *fp = fopen ("tmp-rc2-plain.der", "wb"); */ -/* if (!fp || fwrite (p, n, 1, fp) != 1) */ -/* exit (2); */ -/* fclose (fp); */ -/* } */ - where = "outer.outer.seq"; if (parse_tag (&p, &n, &ti)) { @@ -899,6 +1010,34 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, return -1; } + +/* Return true if the decryption of a bag_data object has likely + succeeded. */ +static int +bag_data_p (const void *plaintext, size_t length) +{ + struct tag_info ti; + const unsigned char *p = plaintext; + size_t n = length; + +/* { */ +/* # warning debug code is enabled */ +/* FILE *fp = fopen ("tmp-3des-plain-key.der", "wb"); */ +/* if (!fp || fwrite (p, n, 1, fp) != 1) */ +/* exit (2); */ +/* fclose (fp); */ +/* } */ + + if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) + return 0; + if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER + || ti.length != 1 || *p) + return 0; + + return 1; +} + + static gcry_mpi_t * parse_bag_data (const unsigned char *buffer, size_t length, int startoffset, size_t *r_consumed, const char *pw) @@ -1028,22 +1167,14 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset, log_error ("error allocating decryption buffer\n"); goto bailout; } - memcpy (plain, p, ti.length); consumed += p - p_start + ti.length; - crypt_block (plain, ti.length, salt, saltlen, iter, pw, GCRY_CIPHER_3DES, 0); + decrypt_block (p, plain, ti.length, salt, saltlen, iter, pw, + GCRY_CIPHER_3DES, + bag_data_p); n = ti.length; startoffset = 0; p_start = p = plain; -/* { */ -/* # warning debug code is enabled */ -/* FILE *fp = fopen ("tmp-rc2-plain-key.der", "wb"); */ -/* if (!fp || fwrite (p, n, 1, fp) != 1) */ -/* exit (2); */ -/* fclose (fp); */ -/* } */ - - where = "decrypted-text"; if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) goto bailout;