From d035d2a52e55c39f346a7d7ab44cf2dbe646eeef Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 21 Jul 2006 09:41:11 +0000 Subject: [PATCH] Support import from TrustedMIME (i.e. from simple keyBags) --- NEWS | 2 + agent/ChangeLog | 7 ++ agent/minip12.c | 237 +++++++++++++++++++++++++++++++++++++++--------- doc/gpl.texi | 4 +- 4 files changed, 203 insertions(+), 47 deletions(-) diff --git a/NEWS b/NEWS index 7fafd44d3..e6cfb2b4a 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,8 @@ Noteworthy changes in version 1.9.22 ------------------------------------------------- + * Enhanced pkcs#12 support to allow import from simple keyBags. + Noteworthy changes in version 1.9.21 (2006-06-20) ------------------------------------------------- diff --git a/agent/ChangeLog b/agent/ChangeLog index a194e0d2e..e3d69d8b4 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,10 @@ +2006-07-21 Werner Koch + + * minip12.c (oid_pkcs_12_keyBag): New. + (parse_bag_encrypted_data): New arg R_RESULT. Support keybags and + return the key object. + (p12_parse): Take new arg into account. Free RESULT on error. + 2006-06-26 Werner Koch * gpg-agent.c (handle_signal): Print infor for SIGUSR2 only in diff --git a/agent/minip12.c b/agent/minip12.c index 912d387d8..243116d25 100644 --- a/agent/minip12.c +++ b/agent/minip12.c @@ -88,6 +88,8 @@ static unsigned char const oid_data[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 }; static unsigned char const oid_encryptedData[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x06 }; +static unsigned char const oid_pkcs_12_keyBag[11] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x01 }; static unsigned char const oid_pkcs_12_pkcs_8ShroudedKeyBag[11] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x02 }; static unsigned char const oid_pkcs_12_CertBag[11] = { @@ -465,11 +467,13 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen, +/* Note: If R_RESULT is passed as NULL, a key object as already be + processed and thus we need to skip it here. */ static int parse_bag_encrypted_data (const unsigned char *buffer, size_t length, int startoffset, size_t *r_consumed, const char *pw, void (*certcb)(void*, const unsigned char*, size_t), - void *certcbarg) + void *certcbarg, gcry_mpi_t **r_result) { struct tag_info ti; const unsigned char *p = buffer; @@ -483,7 +487,12 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, int bad_pass = 0; unsigned char *cram_buffer = NULL; size_t consumed = 0; /* Number of bytes consumed from the orginal buffer. */ - + int is_3des = 0; + gcry_mpi_t *result = NULL; + int result_count; + + if (r_result) + *r_result = NULL; where = "start"; if (parse_tag (&p, &n, &ti)) goto bailout; @@ -529,10 +538,19 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, p += DIM(oid_pbeWithSHAAnd40BitRC2_CBC); n -= DIM(oid_pbeWithSHAAnd40BitRC2_CBC); } + else if (!ti.class && ti.tag == TAG_OBJECT_ID + && ti.length == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC) + && !memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC, + DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC))) + { + p += DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC); + n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC); + is_3des = 1; + } else goto bailout; - where = "rc2-params"; + where = "rc2or3des-params"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) @@ -557,7 +575,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, n--; } - where = "rc2-ciphertext"; + where = "rc2or3des-ciphertext"; if (parse_tag (&p, &n, &ti)) goto bailout; @@ -566,7 +584,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, { /* Mozilla exported certs now come with single byte chunks of octect strings. (Mozilla Firefox 1.0.4). Arghh. */ - where = "cram-rc2-ciphertext"; + where = "cram-rc2or3des-ciphertext"; cram_buffer = cram_octet_string ( p, &n, &consumed); if (!cram_buffer) goto bailout; @@ -581,7 +599,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, else goto bailout; - log_info ("%lu bytes of RC2 encrypted text\n", ti.length); + log_info ("%lu bytes of %s encrypted text\n",ti.length,is_3des?"3DES":"RC2"); plain = gcry_malloc_secure (ti.length); if (!plain) @@ -591,7 +609,9 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, } memcpy (plain, p, ti.length); crypt_block (plain, ti.length, salt, saltlen, - iter, pw, GCRY_CIPHER_RFC2268_40, 0); + iter, pw, + is_3des? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40, + 0); n = ti.length; startoffset = 0; p_start = p = plain; @@ -625,7 +645,8 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, /* Loop over all certificates inside the bag. */ while (n) { - int isbag = 0; + int iscrlbag = 0; + int iskeybag = 0; where = "certbag.nextcert"; if (ti.class || ti.tag != TAG_SEQUENCE) @@ -647,7 +668,17 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, { p += DIM(oid_pkcs_12_CrlBag); n -= DIM(oid_pkcs_12_CrlBag); - isbag = 1; + iscrlbag = 1; + } + else if ( ti.length == DIM(oid_pkcs_12_keyBag) + && !memcmp (p, oid_pkcs_12_keyBag, DIM(oid_pkcs_12_keyBag))) + { + /* The TrustedMIME plugin for MS Outlook started to create + files with just one outer 3DES encrypted container and + inside the certificates as well as the key. */ + p += DIM(oid_pkcs_12_keyBag); + n -= DIM(oid_pkcs_12_keyBag); + iskeybag = 1; } else goto bailout; @@ -657,14 +688,106 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, goto bailout; if (ti.class != CONTEXT || ti.tag) goto bailout; - if (isbag) + if (iscrlbag) { log_info ("skipping unsupported crlBag\n"); p += ti.length; n -= ti.length; } + else if (iskeybag && (result || !r_result)) + { + log_info ("one keyBag already processed; skipping this one\n"); + p += ti.length; + n -= ti.length; + } + else if (iskeybag) + { + int len; + + log_info ("processing simple keyBag\n"); + + /* Fixme: This code is duplicated from parse_bag_data. */ + if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) + goto bailout; + if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER + || ti.length != 1 || *p) + goto bailout; + p++; n--; + if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) + goto bailout; + len = ti.length; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (len < ti.nhdr) + goto bailout; + len -= ti.nhdr; + if (ti.class || ti.tag != TAG_OBJECT_ID + || ti.length != DIM(oid_rsaEncryption) + || memcmp (p, oid_rsaEncryption, + DIM(oid_rsaEncryption))) + goto bailout; + p += DIM (oid_rsaEncryption); + n -= DIM (oid_rsaEncryption); + if (len < ti.length) + goto bailout; + len -= ti.length; + if (n < len) + goto bailout; + p += len; + n -= len; + if ( parse_tag (&p, &n, &ti) + || ti.class || ti.tag != TAG_OCTET_STRING) + goto bailout; + if ( parse_tag (&p, &n, &ti) + || ti.class || ti.tag != TAG_SEQUENCE) + goto bailout; + len = ti.length; + + result = gcry_calloc (10, sizeof *result); + if (!result) + { + log_error ( "error allocating result array\n"); + goto bailout; + } + result_count = 0; + + where = "reading.keybag.key-parameters"; + for (result_count = 0; len && result_count < 9;) + { + if ( parse_tag (&p, &n, &ti) + || ti.class || ti.tag != TAG_INTEGER) + goto bailout; + if (len < ti.nhdr) + goto bailout; + len -= ti.nhdr; + if (len < ti.length) + goto bailout; + len -= ti.length; + if (!result_count && ti.length == 1 && !*p) + ; /* ignore the very first one if it is a 0 */ + else + { + int rc; + + rc = gcry_mpi_scan (result+result_count, GCRYMPI_FMT_USG, p, + ti.length, NULL); + if (rc) + { + log_error ("error parsing key parameter: %s\n", + gpg_strerror (rc)); + goto bailout; + } + result_count++; + } + p += ti.length; + n -= ti.length; + } + if (len) + goto bailout; + } else { + log_info ("processing certBag\n"); if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) @@ -730,9 +853,19 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, *r_consumed = consumed; gcry_free (plain); gcry_free (cram_buffer); + if (r_result) + *r_result = result; return 0; bailout: + if (result) + { + int i; + + for (i=0; result[i]; i++) + gcry_mpi_release (result[i]); + gcry_free (result); + } if (r_consumed) *r_consumed = consumed; gcry_free (plain); @@ -1066,7 +1199,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, bagseqlength = ti.length; while (bagseqlength || bagseqndef) { - log_debug ( "at offset %u\n", (p - p_start)); +/* log_debug ( "at offset %u\n", (p - p_start)); */ where = "bag-sequence"; if (parse_tag (&p, &n, &ti)) goto bailout; @@ -1105,7 +1238,8 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, len -= DIM(oid_encryptedData); where = "bag.encryptedData"; if (parse_bag_encrypted_data (p, n, (p - p_start), &consumed, pw, - certcb, certcbarg)) + certcb, certcbarg, + result? NULL : &result)) goto bailout; if (lenndef) len += consumed; @@ -1115,7 +1249,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, { if (result) { - log_info ("already got an data object, skipping next one\n"); + log_info ("already got an key object, skipping this one\n"); p += ti.length; n -= ti.length; } @@ -1159,7 +1293,14 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, return result; bailout: log_error ("error at \"%s\", offset %u\n", where, (p - p_start)); - /* fixme: need to release RESULT. */ + if (result) + { + int i; + + for (i=0; result[i]; i++) + gcry_mpi_release (result[i]); + gcry_free (result); + } gcry_free (cram_buffer); return NULL; } @@ -1227,6 +1368,8 @@ create_final (struct buffer_s *sequences, const char *pw, size_t *r_length) unsigned char keybuf[20]; gcry_md_hd_t md; int rc; + int with_mac = 1; + /* 9 steps to create the pkcs#12 Krampf. */ @@ -1264,7 +1407,8 @@ create_final (struct buffer_s *sequences, const char *pw, size_t *r_length) needed += 3; /* 0. And the final outer sequence. */ - needed += DIM (data_mactemplate); + if (with_mac) + needed += DIM (data_mactemplate); len[0] = needed; n = compute_tag_length (needed); needed += n; @@ -1311,37 +1455,40 @@ create_final (struct buffer_s *sequences, const char *pw, size_t *r_length) p += sequences[i].length; } - /* Intermezzo to compute the MAC. */ - maclen = p - macstart; - gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); - if (string_to_key (3, salt, 8, 2048, pw, 20, keybuf)) + if (with_mac) { - gcry_free (result); - return NULL; - } - rc = gcry_md_open (&md, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC); - if (rc) - { - log_error ("gcry_md_open failed: %s\n", gpg_strerror (rc)); - gcry_free (result); - return NULL; - } - rc = gcry_md_setkey (md, keybuf, 20); - if (rc) - { - log_error ("gcry_md_setkey failed: %s\n", gpg_strerror (rc)); - gcry_md_close (md); - gcry_free (result); - return NULL; - } - gcry_md_write (md, macstart, maclen); + /* Intermezzo to compute the MAC. */ + maclen = p - macstart; + gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); + if (string_to_key (3, salt, 8, 2048, pw, 20, keybuf)) + { + gcry_free (result); + return NULL; + } + rc = gcry_md_open (&md, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC); + if (rc) + { + log_error ("gcry_md_open failed: %s\n", gpg_strerror (rc)); + gcry_free (result); + return NULL; + } + rc = gcry_md_setkey (md, keybuf, 20); + if (rc) + { + log_error ("gcry_md_setkey failed: %s\n", gpg_strerror (rc)); + gcry_md_close (md); + gcry_free (result); + return NULL; + } + gcry_md_write (md, macstart, maclen); - /* 8. Append the MAC template and fix it up. */ - memcpy (p, data_mactemplate, DIM (data_mactemplate)); - memcpy (p + DATA_MACTEMPLATE_SALT_OFF, salt, 8); - memcpy (p + DATA_MACTEMPLATE_MAC_OFF, gcry_md_read (md, 0), 20); - p += DIM (data_mactemplate); - gcry_md_close (md); + /* 8. Append the MAC template and fix it up. */ + memcpy (p, data_mactemplate, DIM (data_mactemplate)); + memcpy (p + DATA_MACTEMPLATE_SALT_OFF, salt, 8); + memcpy (p + DATA_MACTEMPLATE_MAC_OFF, gcry_md_read (md, 0), 20); + p += DIM (data_mactemplate); + gcry_md_close (md); + } /* Ready. */ resultlen = p - result; @@ -1952,7 +2099,7 @@ main (int argc, char **argv) /* Local Variables: -compile-command: "gcc -Wall -O -g -DTEST=1 -o minip12 minip12.c ../jnlib/libjnlib.a -L /usr/local/lib -lgcrypt -lgpg-error" +compile-command: "gcc -Wall -O0 -g -DTEST=1 -o minip12 minip12.c ../jnlib/libjnlib.a -L /usr/local/lib -lgcrypt -lgpg-error" End: */ #endif /* TEST */ diff --git a/doc/gpl.texi b/doc/gpl.texi index ca0508fad..00108d230 100644 --- a/doc/gpl.texi +++ b/doc/gpl.texi @@ -6,7 +6,7 @@ @display Copyright @copyright{} 1989, 1991 Free Software Foundation, Inc. -59 Temple Place -- Suite 330, Boston, MA 02111-1307, USA +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -352,7 +352,7 @@ 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. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @end smallexample Also add information on how to contact you by electronic and paper mail.