diff --git a/sm/import.c b/sm/import.c index ca693824a..4c699c2dd 100644 --- a/sm/import.c +++ b/sm/import.c @@ -1,939 +1,986 @@ /* import.c - Import certificates * Copyright (C) 2001, 2003, 2004, 2009, 2010 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 3 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, see . */ #include #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/exechelp.h" #include "../common/i18n.h" #include "../common/sysutils.h" #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */ #include "../common/membuf.h" #include "minip12.h" /* The arbitrary limit of one PKCS#12 object. */ #define MAX_P12OBJ_SIZE 128 /*kb*/ struct stats_s { unsigned long count; unsigned long imported; unsigned long unchanged; unsigned long not_imported; unsigned long secret_read; unsigned long secret_imported; unsigned long secret_dups; }; struct rsa_secret_key_s { gcry_mpi_t n; /* public modulus */ gcry_mpi_t e; /* public exponent */ gcry_mpi_t d; /* exponent */ gcry_mpi_t p; /* prime p. */ gcry_mpi_t q; /* prime q. */ gcry_mpi_t u; /* inverse of p mod q. */ }; static gpg_error_t parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats); static void print_imported_status (ctrl_t ctrl, ksba_cert_t cert, int new_cert) { char *fpr; fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); if (new_cert) gpgsm_status2 (ctrl, STATUS_IMPORTED, fpr, "[X.509]", NULL); gpgsm_status2 (ctrl, STATUS_IMPORT_OK, new_cert? "1":"0", fpr, NULL); xfree (fpr); } /* Print an IMPORT_PROBLEM status. REASON is one of: 0 := "No specific reason given". 1 := "Invalid Certificate". 2 := "Issuer Certificate missing". 3 := "Certificate Chain too long". 4 := "Error storing certificate". */ static void print_import_problem (ctrl_t ctrl, ksba_cert_t cert, int reason) { char *fpr = NULL; char buf[25]; int i; sprintf (buf, "%d", reason); if (cert) { fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); /* detetect an error (all high) value */ for (i=0; fpr[i] == 'F'; i++) ; if (!fpr[i]) { xfree (fpr); fpr = NULL; } } gpgsm_status2 (ctrl, STATUS_IMPORT_PROBLEM, buf, fpr, NULL); xfree (fpr); } void print_imported_summary (ctrl_t ctrl, struct stats_s *stats) { char buf[14*25]; if (!opt.quiet) { log_info (_("total number processed: %lu\n"), stats->count); if (stats->imported) { log_info (_(" imported: %lu"), stats->imported ); log_printf ("\n"); } if (stats->unchanged) log_info (_(" unchanged: %lu\n"), stats->unchanged); if (stats->secret_read) log_info (_(" secret keys read: %lu\n"), stats->secret_read ); if (stats->secret_imported) log_info (_(" secret keys imported: %lu\n"), stats->secret_imported ); if (stats->secret_dups) log_info (_(" secret keys unchanged: %lu\n"), stats->secret_dups ); if (stats->not_imported) log_info (_(" not imported: %lu\n"), stats->not_imported); } sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", stats->count, 0l /*stats->no_user_id*/, stats->imported, 0l /*stats->imported_rsa*/, stats->unchanged, 0l /*stats->n_uids*/, 0l /*stats->n_subk*/, 0l /*stats->n_sigs*/, 0l /*stats->n_revoc*/, stats->secret_read, stats->secret_imported, stats->secret_dups, 0l /*stats->skipped_new_keys*/, stats->not_imported ); gpgsm_status (ctrl, STATUS_IMPORT_RES, buf); } static void check_and_store (ctrl_t ctrl, struct stats_s *stats, ksba_cert_t cert, int depth) { int rc; if (stats) stats->count++; if ( depth >= 50 ) { log_error (_("certificate chain too long\n")); if (stats) stats->not_imported++; print_import_problem (ctrl, cert, 3); return; } /* Some basic checks, but don't care about missing certificates; this is so that we are able to import entire certificate chains w/o requiring a special order (i.e. root-CA first). This used to be different but because gpgsm_verify even imports certificates without any checks, it doesn't matter much and the code gets much cleaner. A housekeeping function to remove certificates w/o an anchor would be nice, though. Optionally we do a full validation in addition to the basic test. */ rc = gpgsm_basic_cert_check (ctrl, cert); if (!rc && ctrl->with_validation) rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL); if (!rc || (!ctrl->with_validation && (gpg_err_code (rc) == GPG_ERR_MISSING_CERT || gpg_err_code (rc) == GPG_ERR_MISSING_ISSUER_CERT))) { int existed; if (!keydb_store_cert (ctrl, cert, 0, &existed)) { ksba_cert_t next = NULL; if (!existed) { print_imported_status (ctrl, cert, 1); if (stats) stats->imported++; } else { print_imported_status (ctrl, cert, 0); if (stats) stats->unchanged++; } if (opt.verbose > 1 && existed) { if (depth) log_info ("issuer certificate already in DB\n"); else log_info ("certificate already in DB\n"); } else if (opt.verbose && !existed) { if (depth) log_info ("issuer certificate imported\n"); else log_info ("certificate imported\n"); } /* Now lets walk up the chain and import all certificates up the chain. This is required in case we already stored parent certificates in the ephemeral keybox. Do not update the statistics, though. */ if (!gpgsm_walk_cert_chain (ctrl, cert, &next)) { check_and_store (ctrl, NULL, next, depth+1); ksba_cert_release (next); } } else { log_error (_("error storing certificate\n")); if (stats) stats->not_imported++; print_import_problem (ctrl, cert, 4); } } else { log_error (_("basic certificate checks failed - not imported\n")); if (stats) stats->not_imported++; /* We keep the test for GPG_ERR_MISSING_CERT only in case GPG_ERR_MISSING_CERT has been used instead of the newer GPG_ERR_MISSING_ISSUER_CERT. */ print_import_problem (ctrl, cert, gpg_err_code (rc) == GPG_ERR_MISSING_ISSUER_CERT? 2 : gpg_err_code (rc) == GPG_ERR_MISSING_CERT? 2 : gpg_err_code (rc) == GPG_ERR_BAD_CERT? 1 : 0); } } static int import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd) { int rc; gnupg_ksba_io_t b64reader = NULL; ksba_reader_t reader; ksba_cert_t cert = NULL; ksba_cms_t cms = NULL; estream_t fp = NULL; ksba_content_type_t ct; int any = 0; fp = es_fdopen_nc (in_fd, "rb"); if (!fp) { rc = gpg_error_from_syserror (); log_error ("fdopen() failed: %s\n", strerror (errno)); goto leave; } rc = gnupg_ksba_create_reader (&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0) | (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0) | GNUPG_KSBA_IO_MULTIPEM), fp, &reader); if (rc) { log_error ("can't create reader: %s\n", gpg_strerror (rc)); goto leave; } /* We need to loop here to handle multiple PEM objects in one file. */ do { ksba_cms_release (cms); cms = NULL; ksba_cert_release (cert); cert = NULL; ct = ksba_cms_identify (reader); if (ct == KSBA_CT_SIGNED_DATA) { /* This is probably a signed-only message - import the certs */ ksba_stop_reason_t stopreason; int i; rc = ksba_cms_new (&cms); if (rc) goto leave; rc = ksba_cms_set_reader_writer (cms, reader, NULL); if (rc) { log_error ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (rc)); goto leave; } do { rc = ksba_cms_parse (cms, &stopreason); if (rc) { log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc)); goto leave; } if (stopreason == KSBA_SR_BEGIN_DATA) log_info ("not a certs-only message\n"); } while (stopreason != KSBA_SR_READY); for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++) { check_and_store (ctrl, stats, cert, 0); ksba_cert_release (cert); cert = NULL; } if (!i) log_error ("no certificate found\n"); else any = 1; } else if (ct == KSBA_CT_PKCS12) { /* This seems to be a pkcs12 message. */ rc = parse_p12 (ctrl, reader, stats); if (!rc) any = 1; } else if (ct == KSBA_CT_NONE) { /* Failed to identify this message - assume a certificate */ rc = ksba_cert_new (&cert); if (rc) goto leave; rc = ksba_cert_read_der (cert, reader); if (rc) goto leave; check_and_store (ctrl, stats, cert, 0); any = 1; } else { log_error ("can't extract certificates from input\n"); rc = gpg_error (GPG_ERR_NO_DATA); } ksba_reader_clear (reader, NULL, NULL); } while (!gnupg_ksba_reader_eof_seen (b64reader)); leave: if (any && gpg_err_code (rc) == GPG_ERR_EOF) rc = 0; ksba_cms_release (cms); ksba_cert_release (cert); gnupg_ksba_destroy_reader (b64reader); es_fclose (fp); return rc; } /* Re-import certifciates. IN_FD is a list of linefeed delimited fingerprints t re-import. The actual re-import is done by clearing the ephemeral flag. */ static int reimport_one (ctrl_t ctrl, struct stats_s *stats, int in_fd) { gpg_error_t err = 0; estream_t fp = NULL; char line[100]; /* Sufficient for a fingerprint. */ KEYDB_HANDLE kh; KEYDB_SEARCH_DESC desc; ksba_cert_t cert = NULL; unsigned int flags; kh = keydb_new (); if (!kh) { err = gpg_error (GPG_ERR_ENOMEM);; log_error (_("failed to allocate keyDB handle\n")); goto leave; } keydb_set_ephemeral (kh, 1); fp = es_fdopen_nc (in_fd, "r"); if (!fp) { err = gpg_error_from_syserror (); log_error ("es_fdopen(%d) failed: %s\n", in_fd, gpg_strerror (err)); goto leave; } while (es_fgets (line, DIM(line)-1, fp) ) { if (*line && line[strlen(line)-1] != '\n') { err = gpg_error (GPG_ERR_LINE_TOO_LONG); goto leave; } trim_spaces (line); if (!*line) continue; stats->count++; err = classify_user_id (line, &desc, 0); if (err) { print_import_problem (ctrl, NULL, 0); stats->not_imported++; continue; } keydb_search_reset (kh); err = keydb_search (ctrl, kh, &desc, 1); if (err) { print_import_problem (ctrl, NULL, 0); stats->not_imported++; continue; } ksba_cert_release (cert); cert = NULL; err = keydb_get_cert (kh, &cert); if (err) { log_error ("keydb_get_cert() failed: %s\n", gpg_strerror (err)); print_import_problem (ctrl, NULL, 1); stats->not_imported++; continue; } err = keydb_get_flags (kh, KEYBOX_FLAG_BLOB, 0, &flags); if (err) { log_error (_("error getting stored flags: %s\n"), gpg_strerror (err)); print_imported_status (ctrl, cert, 0); stats->not_imported++; continue; } if ( !(flags & KEYBOX_FLAG_BLOB_EPHEMERAL) ) { print_imported_status (ctrl, cert, 0); stats->unchanged++; continue; } err = keydb_set_cert_flags (ctrl, cert, 1, KEYBOX_FLAG_BLOB, 0, KEYBOX_FLAG_BLOB_EPHEMERAL, 0); if (err) { log_error ("clearing ephemeral flag failed: %s\n", gpg_strerror (err)); print_import_problem (ctrl, cert, 0); stats->not_imported++; continue; } print_imported_status (ctrl, cert, 1); stats->imported++; } err = 0; if (es_ferror (fp)) { err = gpg_error_from_syserror (); log_error ("error reading fd %d: %s\n", in_fd, gpg_strerror (err)); goto leave; } leave: ksba_cert_release (cert); keydb_release (kh); es_fclose (fp); return err; } int gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode) { int rc; struct stats_s stats; memset (&stats, 0, sizeof stats); if (reimport_mode) rc = reimport_one (ctrl, &stats, in_fd); else rc = import_one (ctrl, &stats, in_fd); print_imported_summary (ctrl, &stats); /* If we never printed an error message do it now so that a command line invocation will return with an error (log_error keeps a global errorcount) */ if (rc && !log_get_errorcount (0)) log_error (_("error importing certificate: %s\n"), gpg_strerror (rc)); return rc; } int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files, int (*of)(const char *fname)) { int rc = 0; struct stats_s stats; memset (&stats, 0, sizeof stats); if (!nfiles) rc = import_one (ctrl, &stats, 0); else { for (; nfiles && !rc ; nfiles--, files++) { int fd = of (*files); rc = import_one (ctrl, &stats, fd); close (fd); if (rc == -1) rc = 0; } } print_imported_summary (ctrl, &stats); /* If we never printed an error message do it now so that a command line invocation will return with an error (log_error keeps a global errorcount) */ if (rc && !log_get_errorcount (0)) log_error (_("error importing certificate: %s\n"), gpg_strerror (rc)); return rc; } /* Check that the RSA secret key SKEY is valid. Swap parameters to the libgcrypt standard. */ static gpg_error_t rsa_key_check (struct rsa_secret_key_s *skey) { int err = 0; gcry_mpi_t t = gcry_mpi_snew (0); gcry_mpi_t t1 = gcry_mpi_snew (0); gcry_mpi_t t2 = gcry_mpi_snew (0); gcry_mpi_t phi = gcry_mpi_snew (0); /* Check that n == p * q. */ gcry_mpi_mul (t, skey->p, skey->q); if (gcry_mpi_cmp( t, skey->n) ) { log_error ("RSA oops: n != p * q\n"); err++; } /* Check that p is less than q. */ if (gcry_mpi_cmp (skey->p, skey->q) > 0) { gcry_mpi_t tmp; log_info ("swapping secret primes\n"); tmp = gcry_mpi_copy (skey->p); gcry_mpi_set (skey->p, skey->q); gcry_mpi_set (skey->q, tmp); gcry_mpi_release (tmp); /* Recompute u. */ gcry_mpi_invm (skey->u, skey->p, skey->q); } /* Check that e divides neither p-1 nor q-1. */ gcry_mpi_sub_ui (t, skey->p, 1 ); gcry_mpi_div (NULL, t, t, skey->e, 0); if (!gcry_mpi_cmp_ui( t, 0) ) { log_error ("RSA oops: e divides p-1\n"); err++; } gcry_mpi_sub_ui (t, skey->q, 1); gcry_mpi_div (NULL, t, t, skey->e, 0); if (!gcry_mpi_cmp_ui( t, 0)) { log_info ("RSA oops: e divides q-1\n" ); err++; } /* Check that d is correct. */ gcry_mpi_sub_ui (t1, skey->p, 1); gcry_mpi_sub_ui (t2, skey->q, 1); gcry_mpi_mul (phi, t1, t2); gcry_mpi_invm (t, skey->e, phi); if (gcry_mpi_cmp (t, skey->d)) { /* No: try universal exponent. */ gcry_mpi_gcd (t, t1, t2); gcry_mpi_div (t, NULL, phi, t, 0); gcry_mpi_invm (t, skey->e, t); if (gcry_mpi_cmp (t, skey->d)) { log_error ("RSA oops: bad secret exponent\n"); err++; } } /* Check for correctness of u. */ gcry_mpi_invm (t, skey->p, skey->q); if (gcry_mpi_cmp (t, skey->u)) { log_info ("RSA oops: bad u parameter\n"); err++; } if (err) log_info ("RSA secret key check failed\n"); gcry_mpi_release (t); gcry_mpi_release (t1); gcry_mpi_release (t2); gcry_mpi_release (phi); return err? gpg_error (GPG_ERR_BAD_SECKEY):0; } /* Object passed to store_cert_cb. */ struct store_cert_parm_s { gpg_error_t err; /* First error seen. */ struct stats_s *stats; /* The stats object. */ ctrl_t ctrl; /* The control object. */ }; /* Helper to store the DER encoded certificate CERTDATA of length CERTDATALEN. */ static void store_cert_cb (void *opaque, const unsigned char *certdata, size_t certdatalen) { struct store_cert_parm_s *parm = opaque; gpg_error_t err; ksba_cert_t cert; err = ksba_cert_new (&cert); if (err) { if (!parm->err) parm->err = err; return; } err = ksba_cert_init_from_mem (cert, certdata, certdatalen); if (err) { log_error ("failed to parse a certificate: %s\n", gpg_strerror (err)); if (!parm->err) parm->err = err; } else check_and_store (parm->ctrl, parm->stats, cert, 0); ksba_cert_release (cert); } /* Assume that the reader is at a pkcs#12 message and try to import certificates from that stupid format. We will transfer secret keys to the agent. */ static gpg_error_t parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats) { gpg_error_t err = 0; char buffer[1024]; size_t ntotal, nread; membuf_t p12mbuf; char *p12buffer = NULL; size_t p12buflen; size_t p12bufoff; gcry_mpi_t *kparms = NULL; struct rsa_secret_key_s sk; char *passphrase = NULL; unsigned char *key = NULL; size_t keylen; void *kek = NULL; size_t keklen; unsigned char *wrappedkey = NULL; size_t wrappedkeylen; gcry_cipher_hd_t cipherhd = NULL; gcry_sexp_t s_key = NULL; unsigned char grip[20]; int bad_pass = 0; + char *curve = NULL; int i; struct store_cert_parm_s store_cert_parm; memset (&store_cert_parm, 0, sizeof store_cert_parm); store_cert_parm.ctrl = ctrl; store_cert_parm.stats = stats; init_membuf (&p12mbuf, 4096); ntotal = 0; while (!(err = ksba_reader_read (reader, buffer, sizeof buffer, &nread))) { if (ntotal >= MAX_P12OBJ_SIZE*1024) { /* Arbitrary limit to avoid DoS attacks. */ err = gpg_error (GPG_ERR_TOO_LARGE); log_error ("pkcs#12 object is larger than %dk\n", MAX_P12OBJ_SIZE); break; } put_membuf (&p12mbuf, buffer, nread); ntotal += nread; } if (gpg_err_code (err) == GPG_ERR_EOF) err = 0; if (!err) { p12buffer = get_membuf (&p12mbuf, &p12buflen); if (!p12buffer) err = gpg_error_from_syserror (); } if (err) { log_error (_("error reading input: %s\n"), gpg_strerror (err)); goto leave; } /* GnuPG 2.0.4 accidentally created binary P12 files with the string "The passphrase is %s encoded.\n\n" prepended to the ASN.1 data. We fix that here. */ if (p12buflen > 29 && !memcmp (p12buffer, "The passphrase is ", 18)) { for (p12bufoff=18; p12bufoff < p12buflen && p12buffer[p12bufoff] != '\n'; p12bufoff++) ; p12bufoff++; if (p12bufoff < p12buflen && p12buffer[p12bufoff] == '\n') p12bufoff++; } else p12bufoff = 0; err = gpgsm_agent_ask_passphrase (ctrl, i18n_utf8 ("Please enter the passphrase to unprotect the PKCS#12 object."), 0, &passphrase); if (err) goto leave; kparms = p12_parse (p12buffer + p12bufoff, p12buflen - p12bufoff, - passphrase, store_cert_cb, &store_cert_parm, &bad_pass); + passphrase, store_cert_cb, + &store_cert_parm, &bad_pass, &curve); xfree (passphrase); passphrase = NULL; if (!kparms) { log_error ("error parsing or decrypting the PKCS#12 file\n"); err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } -/* print_mpi (" n", kparms[0]); */ -/* print_mpi (" e", kparms[1]); */ -/* print_mpi (" d", kparms[2]); */ -/* print_mpi (" p", kparms[3]); */ -/* print_mpi (" q", kparms[4]); */ -/* print_mpi ("dmp1", kparms[5]); */ -/* print_mpi ("dmq1", kparms[6]); */ -/* print_mpi (" u", kparms[7]); */ - - sk.n = kparms[0]; - sk.e = kparms[1]; - sk.d = kparms[2]; - sk.q = kparms[3]; - sk.p = kparms[4]; - sk.u = kparms[7]; - err = rsa_key_check (&sk); - if (err) - goto leave; -/* print_mpi (" n", sk.n); */ -/* print_mpi (" e", sk.e); */ -/* print_mpi (" d", sk.d); */ -/* print_mpi (" p", sk.p); */ -/* print_mpi (" q", sk.q); */ -/* print_mpi (" u", sk.u); */ - - /* Create an S-expression from the parameters. */ - err = gcry_sexp_build (&s_key, NULL, - "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", - sk.n, sk.e, sk.d, sk.p, sk.q, sk.u, NULL); + if (curve) + { + gcry_ctx_t ecctx = NULL; + + /* log_debug ("curve: %s\n", curve); */ + /* gcry_log_debugmpi ("MPI[0]", kparms[0]); */ + + /* We need to get the public key. */ + err = gcry_mpi_ec_new (&ecctx, NULL, curve); + if (err) + { + log_error ("error creating context for curve '%s': %s\n", + curve, gpg_strerror (err)); + goto leave; + } + err = gcry_mpi_ec_set_mpi ("d", kparms[0], ecctx); + if (err) + { + log_error ("error setting 'd' into context of curve '%s': %s\n", + curve, gpg_strerror (err)); + gcry_ctx_release (ecctx); + goto leave; + } + + kparms[1] = gcry_mpi_ec_get_mpi ("q", ecctx, 1); + if (!kparms[1]) + { + log_error ("error computing 'q' from 'd' for curve '%s'\n", curve); + gcry_ctx_release (ecctx); + goto leave; + } + + gcry_ctx_release (ecctx); + + err = gcry_sexp_build (&s_key, NULL, + "(private-key(ecc(curve %s)(q%m)(d%m)))", + curve, kparms[1], kparms[0], NULL); + } + else /* RSA */ + { + /* print_mpi (" n", kparms[0]); */ + /* print_mpi (" e", kparms[1]); */ + /* print_mpi (" d", kparms[2]); */ + /* print_mpi (" p", kparms[3]); */ + /* print_mpi (" q", kparms[4]); */ + /* print_mpi ("dmp1", kparms[5]); */ + /* print_mpi ("dmq1", kparms[6]); */ + /* print_mpi (" u", kparms[7]); */ + + sk.n = kparms[0]; + sk.e = kparms[1]; + sk.d = kparms[2]; + sk.q = kparms[3]; + sk.p = kparms[4]; + sk.u = kparms[7]; + err = rsa_key_check (&sk); + if (err) + goto leave; + /* print_mpi (" n", sk.n); */ + /* print_mpi (" e", sk.e); */ + /* print_mpi (" d", sk.d); */ + /* print_mpi (" p", sk.p); */ + /* print_mpi (" q", sk.q); */ + /* print_mpi (" u", sk.u); */ + + /* Create an S-expression from the parameters. */ + err = gcry_sexp_build (&s_key, NULL, + "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", + sk.n, sk.e, sk.d, sk.p, sk.q, sk.u, NULL); + } + + /* The next is very ugly - we really should not rely on our + * knowledge of p12_parse internals. */ for (i=0; i < 8; i++) gcry_mpi_release (kparms[i]); gcry_free (kparms); kparms = NULL; if (err) { log_error ("failed to create S-expression from key: %s\n", gpg_strerror (err)); goto leave; } /* Compute the keygrip. */ if (!gcry_pk_get_keygrip (s_key, grip)) { err = gpg_error (GPG_ERR_GENERAL); log_error ("can't calculate keygrip\n"); goto leave; } log_printhex (grip, 20, "keygrip="); /* Convert to canonical encoding using a function which pads it to a multiple of 64 bits. We need this padding for AESWRAP. */ err = make_canon_sexp_pad (s_key, 1, &key, &keylen); if (err) { log_error ("error creating canonical S-expression\n"); goto leave; } gcry_sexp_release (s_key); s_key = NULL; /* Get the current KEK. */ err = gpgsm_agent_keywrap_key (ctrl, 0, &kek, &keklen); if (err) { log_error ("error getting the KEK: %s\n", gpg_strerror (err)); goto leave; } /* Wrap the key. */ err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0); if (err) goto leave; err = gcry_cipher_setkey (cipherhd, kek, keklen); if (err) goto leave; xfree (kek); kek = NULL; wrappedkeylen = keylen + 8; wrappedkey = xtrymalloc (wrappedkeylen); if (!wrappedkey) { err = gpg_error_from_syserror (); goto leave; } err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen, key, keylen); if (err) goto leave; xfree (key); key = NULL; gcry_cipher_close (cipherhd); cipherhd = NULL; /* Send the wrapped key to the agent. */ err = gpgsm_agent_import_key (ctrl, wrappedkey, wrappedkeylen); if (!err) { stats->count++; stats->secret_read++; stats->secret_imported++; } else if ( gpg_err_code (err) == GPG_ERR_EEXIST ) { err = 0; stats->count++; stats->secret_read++; stats->secret_dups++; } /* If we did not get an error from storing the secret key we return a possible error from parsing the certificates. We do this after storing the secret keys so that a bad certificate does not inhibit our chance to store the secret key. */ if (!err && store_cert_parm.err) err = store_cert_parm.err; leave: if (kparms) { for (i=0; i < 8; i++) gcry_mpi_release (kparms[i]); gcry_free (kparms); kparms = NULL; } xfree (key); gcry_sexp_release (s_key); xfree (passphrase); gcry_cipher_close (cipherhd); xfree (wrappedkey); xfree (kek); xfree (get_membuf (&p12mbuf, NULL)); xfree (p12buffer); + xfree (curve); if (bad_pass) { /* We only write a plain error code and not direct BAD_PASSPHRASE because the pkcs12 parser might issue this message multiple times, BAD_PASSPHRASE in general requires a keyID and parts of the import might actually succeed so that IMPORT_PROBLEM is also not appropriate. */ gpgsm_status_with_err_code (ctrl, STATUS_ERROR, "import.parsep12", GPG_ERR_BAD_PASSPHRASE); } return err; } diff --git a/sm/minip12.c b/sm/minip12.c index 76ce07376..9aeb47057 100644 --- a/sm/minip12.c +++ b/sm/minip12.c @@ -1,2620 +1,2709 @@ /* minip12.c - A minimal pkcs-12 implementation. * Copyright (C) 2002, 2003, 2004, 2006, 2011 Free Software Foundation, Inc. * Copyright (C) 2014 Werner Koch * * 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 3 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, see . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #ifdef TEST #include #include #endif +#include + #include "../common/logging.h" #include "../common/utf8conv.h" #include "minip12.h" #ifndef DIM #define DIM(v) (sizeof(v)/sizeof((v)[0])) #endif enum { UNIVERSAL = 0, APPLICATION = 1, ASNCONTEXT = 2, PRIVATE = 3 }; enum { TAG_NONE = 0, TAG_BOOLEAN = 1, TAG_INTEGER = 2, TAG_BIT_STRING = 3, TAG_OCTET_STRING = 4, TAG_NULL = 5, TAG_OBJECT_ID = 6, TAG_OBJECT_DESCRIPTOR = 7, TAG_EXTERNAL = 8, TAG_REAL = 9, TAG_ENUMERATED = 10, TAG_EMBEDDED_PDV = 11, TAG_UTF8_STRING = 12, TAG_REALTIVE_OID = 13, TAG_SEQUENCE = 16, TAG_SET = 17, TAG_NUMERIC_STRING = 18, TAG_PRINTABLE_STRING = 19, TAG_TELETEX_STRING = 20, TAG_VIDEOTEX_STRING = 21, TAG_IA5_STRING = 22, TAG_UTC_TIME = 23, TAG_GENERALIZED_TIME = 24, TAG_GRAPHIC_STRING = 25, TAG_VISIBLE_STRING = 26, TAG_GENERAL_STRING = 27, TAG_UNIVERSAL_STRING = 28, TAG_CHARACTER_STRING = 29, TAG_BMP_STRING = 30 }; 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] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x03 }; static unsigned char const oid_pkcs_12_CrlBag[11] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x04 }; static unsigned char const oid_pbeWithSHAAnd3_KeyTripleDES_CBC[10] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03 }; static unsigned char const oid_pbeWithSHAAnd40BitRC2_CBC[10] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x06 }; static unsigned char const oid_x509Certificate_for_pkcs_12[10] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x16, 0x01 }; static unsigned char const oid_pkcs5PBKDF2[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0C }; static unsigned char const oid_pkcs5PBES2[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0D }; static unsigned char const oid_aes128_CBC[9] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x02 }; static unsigned char const oid_rsaEncryption[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; - +static unsigned char const oid_pcPublicKey[7] = { + 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 }; static unsigned char const data_3desiter2048[30] = { 0x30, 0x1C, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03, 0x30, 0x0E, 0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x02, 0x08, 0x00 }; #define DATA_3DESITER2048_SALT_OFF 18 static unsigned char const data_rc2iter2048[30] = { 0x30, 0x1C, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x06, 0x30, 0x0E, 0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x02, 0x08, 0x00 }; #define DATA_RC2ITER2048_SALT_OFF 18 static unsigned char const data_mactemplate[51] = { 0x30, 0x31, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x04, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02, 0x02, 0x08, 0x00 }; #define DATA_MACTEMPLATE_MAC_OFF 17 #define DATA_MACTEMPLATE_SALT_OFF 39 static unsigned char const data_attrtemplate[106] = { 0x31, 0x7c, 0x30, 0x55, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x14, 0x31, 0x48, 0x1e, 0x46, 0x00, 0x47, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x50, 0x00, 0x47, 0x00, 0x20, 0x00, 0x65, 0x00, 0x78, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x74, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x63, 0x00, 0x65, 0x00, 0x72, 0x00, 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x30, 0x23, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15, 0x31, 0x16, 0x04, 0x14 }; /* Need to append SHA-1 digest. */ #define DATA_ATTRTEMPLATE_KEYID_OFF 73 struct buffer_s { unsigned char *buffer; size_t length; }; struct tag_info { int class; int is_constructed; unsigned long tag; unsigned long length; /* length part of the TLV */ int nhdr; int ndef; /* It is an indefinite length */ }; /* Parse the buffer at the address BUFFER which is of SIZE and return the tag and the length part from the TLV triplet. Update BUFFER and SIZE on success. Checks that the encoded length does not exhaust the length of the provided buffer. */ static int parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti) { int c; unsigned long tag; const unsigned char *buf = *buffer; size_t length = *size; ti->length = 0; ti->ndef = 0; ti->nhdr = 0; /* Get the tag */ if (!length) return -1; /* premature eof */ c = *buf++; length--; ti->nhdr++; ti->class = (c & 0xc0) >> 6; ti->is_constructed = !!(c & 0x20); tag = c & 0x1f; if (tag == 0x1f) { tag = 0; do { tag <<= 7; if (!length) return -1; /* premature eof */ c = *buf++; length--; ti->nhdr++; tag |= c & 0x7f; } while (c & 0x80); } ti->tag = tag; /* Get the length */ if (!length) return -1; /* prematureeof */ c = *buf++; length--; ti->nhdr++; if ( !(c & 0x80) ) ti->length = c; else if (c == 0x80) ti->ndef = 1; else if (c == 0xff) return -1; /* forbidden length value */ else { unsigned long len = 0; int count = c & 0x7f; for (; count; count--) { len <<= 8; if (!length) return -1; /* premature_eof */ c = *buf++; length--; ti->nhdr++; len |= c & 0xff; } ti->length = len; } if (ti->class == UNIVERSAL && !ti->tag) ti->length = 0; if (ti->length > length) return -1; /* data larger than buffer. */ *buffer = buf; *size = length; return 0; } /* Given an ASN.1 chunk of a structure like: 24 NDEF: OCTET STRING -- This is not passed to us 04 1: OCTET STRING -- INPUT point s to here : 30 04 1: OCTET STRING : 80 [...] 04 2: OCTET STRING : 00 00 : } -- This denotes a Null tag and are the last -- two bytes in INPUT. Create a new buffer with the content of that octet string. INPUT is the original buffer with a length as stored at LENGTH. Returns NULL on error or a new malloced buffer with the length of this new buffer stored at LENGTH and the number of bytes parsed from input are added to the value stored at INPUT_CONSUMED. INPUT_CONSUMED is allowed to be passed as NULL if the caller is not interested in this value. */ static unsigned char * cram_octet_string (const unsigned char *input, size_t *length, size_t *input_consumed) { const unsigned char *s = input; size_t n = *length; unsigned char *output, *d; struct tag_info ti; /* Allocate output buf. We know that it won't be longer than the input buffer. */ d = output = gcry_malloc (n); if (!output) goto bailout; for (;;) { if (parse_tag (&s, &n, &ti)) goto bailout; if (ti.class == UNIVERSAL && ti.tag == TAG_OCTET_STRING && !ti.ndef && !ti.is_constructed) { memcpy (d, s, ti.length); s += ti.length; d += ti.length; n -= ti.length; } else if (ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed) break; /* Ready */ else goto bailout; } *length = d - output; if (input_consumed) *input_consumed += s - input; return output; bailout: if (input_consumed) *input_consumed += s - input; gcry_free (output); return NULL; } static int string_to_key (int id, char *salt, size_t saltlen, int iter, const char *pw, int req_keylen, unsigned char *keybuf) { int rc, i, j; gcry_md_hd_t md; gcry_mpi_t num_b1 = NULL; int pwlen; unsigned char hash[20], buf_b[64], buf_i[128], *p; size_t cur_keylen; size_t n; cur_keylen = 0; pwlen = strlen (pw); if (pwlen > 63/2) { log_error ("password too long\n"); return -1; } if (saltlen < 8) { log_error ("salt too short\n"); return -1; } /* Store salt and password in BUF_I */ p = buf_i; for(i=0; i < 64; i++) *p++ = salt [i%saltlen]; for(i=j=0; i < 64; i += 2) { *p++ = 0; *p++ = pw[j]; if (++j > pwlen) /* Note, that we include the trailing zero */ j = 0; } for (;;) { rc = gcry_md_open (&md, GCRY_MD_SHA1, 0); if (rc) { log_error ( "gcry_md_open failed: %s\n", gpg_strerror (rc)); return rc; } for(i=0; i < 64; i++) gcry_md_putc (md, id); gcry_md_write (md, buf_i, 128); memcpy (hash, gcry_md_read (md, 0), 20); gcry_md_close (md); for (i=1; i < iter; i++) gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash, 20); for (i=0; i < 20 && cur_keylen < req_keylen; i++) keybuf[cur_keylen++] = hash[i]; if (cur_keylen == req_keylen) { gcry_mpi_release (num_b1); return 0; /* ready */ } /* need more bytes. */ for(i=0; i < 64; i++) buf_b[i] = hash[i % 20]; rc = gcry_mpi_scan (&num_b1, GCRYMPI_FMT_USG, buf_b, 64, &n); if (rc) { log_error ( "gcry_mpi_scan failed: %s\n", gpg_strerror (rc)); return -1; } gcry_mpi_add_ui (num_b1, num_b1, 1); for (i=0; i < 128; i += 64) { gcry_mpi_t num_ij; rc = gcry_mpi_scan (&num_ij, GCRYMPI_FMT_USG, buf_i + i, 64, &n); if (rc) { log_error ( "gcry_mpi_scan failed: %s\n", gpg_strerror (rc)); return -1; } gcry_mpi_add (num_ij, num_ij, num_b1); gcry_mpi_clear_highbit (num_ij, 64*8); rc = gcry_mpi_print (GCRYMPI_FMT_USG, buf_i + i, 64, &n, num_ij); if (rc) { log_error ( "gcry_mpi_print failed: %s\n", gpg_strerror (rc)); return -1; } gcry_mpi_release (num_ij); } } } static int set_key_iv (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter, const char *pw, int keybytes) { unsigned char keybuf[24]; int rc; assert (keybytes == 5 || keybytes == 24); if (string_to_key (1, salt, saltlen, iter, pw, keybytes, keybuf)) return -1; rc = gcry_cipher_setkey (chd, keybuf, keybytes); if (rc) { log_error ( "gcry_cipher_setkey failed: %s\n", gpg_strerror (rc)); return -1; } if (string_to_key (2, salt, saltlen, iter, pw, 8, keybuf)) return -1; rc = gcry_cipher_setiv (chd, keybuf, 8); if (rc) { log_error ("gcry_cipher_setiv failed: %s\n", gpg_strerror (rc)); return -1; } return 0; } static int set_key_iv_pbes2 (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter, const void *iv, size_t ivlen, const char *pw, int algo) { unsigned char *keybuf; size_t keylen; int rc; keylen = gcry_cipher_get_algo_keylen (algo); if (!keylen) return -1; keybuf = gcry_malloc_secure (keylen); if (!keybuf) return -1; rc = gcry_kdf_derive (pw, strlen (pw), GCRY_KDF_PBKDF2, GCRY_MD_SHA1, salt, saltlen, iter, keylen, keybuf); if (rc) { log_error ("gcry_kdf_derive failed: %s\n", gpg_strerror (rc)); gcry_free (keybuf); return -1; } rc = gcry_cipher_setkey (chd, keybuf, keylen); gcry_free (keybuf); if (rc) { log_error ("gcry_cipher_setkey failed: %s\n", gpg_strerror (rc)); return -1; } rc = gcry_cipher_setiv (chd, iv, ivlen); if (rc) { log_error ("gcry_cipher_setiv failed: %s\n", gpg_strerror (rc)); return -1; } return 0; } static void crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen, int iter, const void *iv, size_t ivlen, const char *pw, int cipher_algo, int encrypt) { gcry_cipher_hd_t chd; int rc; rc = gcry_cipher_open (&chd, cipher_algo, GCRY_CIPHER_MODE_CBC, 0); if (rc) { log_error ( "gcry_cipher_open failed: %s\n", gpg_strerror(rc)); wipememory (buffer, length); return; } if (cipher_algo == GCRY_CIPHER_AES128 ? set_key_iv_pbes2 (chd, salt, saltlen, iter, iv, ivlen, pw, cipher_algo) : set_key_iv (chd, salt, saltlen, iter, pw, cipher_algo == GCRY_CIPHER_RFC2268_40? 5:24)) { wipememory (buffer, length); goto leave; } rc = encrypt? gcry_cipher_encrypt (chd, buffer, length, NULL, 0) : gcry_cipher_decrypt (chd, buffer, length, NULL, 0); if (rc) { wipememory (buffer, length); log_error ( "en/de-crytion failed: %s\n", gpg_strerror (rc)); goto leave; } leave: gcry_cipher_close (chd); } /* 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 void *iv, size_t ivlen, 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", "IBM437", "IBM850", "EUC-JP", "BIG5", 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]) { jnlib_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 = jnlib_iconv_open (charsets[charsetidx], "utf-8"); if (cd == (jnlib_iconv_t)(-1)) continue; inptr = pw; inbytes = strlen (pw); outptr = convertedpw; outbytes = convertedpwsize - 1; if ( jnlib_iconv (cd, (const char **)&inptr, &inbytes, &outptr, &outbytes) == (size_t)-1) { jnlib_iconv_close (cd); continue; } *outptr = 0; jnlib_iconv_close (cd); log_info ("decryption failed; trying charset '%s'\n", charsets[charsetidx]); } memcpy (plaintext, ciphertext, length); crypt_block (plaintext, length, salt, saltlen, iter, iv, ivlen, 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"); */ + /* FILE *fp = fopen ("tmp-minip12-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. */ 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, gcry_mpi_t **r_result, int *r_badpass) { struct tag_info ti; const unsigned char *p = buffer; const unsigned char *p_start = buffer; size_t n = length; const char *where; char salt[20]; size_t saltlen; char iv[16]; unsigned int iter; unsigned char *plain = NULL; int bad_pass = 0; unsigned char *cram_buffer = NULL; size_t consumed = 0; /* Number of bytes consumed from the original buffer. */ int is_3des = 0; int is_pbes2 = 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; if (ti.class != ASNCONTEXT || ti.tag) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.tag != TAG_SEQUENCE) goto bailout; where = "bag.encryptedData.version"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 0) goto bailout; p++; n--; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.tag != TAG_SEQUENCE) goto bailout; where = "bag.encryptedData.data"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_data) || memcmp (p, oid_data, DIM(oid_data))) goto bailout; p += DIM(oid_data); n -= DIM(oid_data); where = "bag.encryptedData.keyinfo"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (!ti.class && ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_pbeWithSHAAnd40BitRC2_CBC) && !memcmp (p, oid_pbeWithSHAAnd40BitRC2_CBC, DIM(oid_pbeWithSHAAnd40BitRC2_CBC))) { 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 if (!ti.class && ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_pkcs5PBES2) && !memcmp (p, oid_pkcs5PBES2, ti.length)) { p += ti.length; n -= ti.length; is_pbes2 = 1; } else goto bailout; if (is_pbes2) { where = "pkcs5PBES2-params"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (!(!ti.class && ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_pkcs5PBKDF2) && !memcmp (p, oid_pkcs5PBKDF2, ti.length))) goto bailout; /* Not PBKDF2. */ p += ti.length; n -= ti.length; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (!(!ti.class && ti.tag == TAG_OCTET_STRING && ti.length >= 8 && ti.length < sizeof salt)) goto bailout; /* No salt or unsupported length. */ saltlen = ti.length; memcpy (salt, p, saltlen); p += saltlen; n -= saltlen; if (parse_tag (&p, &n, &ti)) goto bailout; if (!(!ti.class && ti.tag == TAG_INTEGER && ti.length)) goto bailout; /* No valid iteration count. */ for (iter=0; ti.length; ti.length--) { iter <<= 8; iter |= (*p++) & 0xff; n--; } /* Note: We don't support the optional parameters but assume that the algorithmIdentifier follows. */ if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (!(!ti.class && ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_aes128_CBC) && !memcmp (p, oid_aes128_CBC, ti.length))) goto bailout; /* Not AES-128. */ p += ti.length; n -= ti.length; if (parse_tag (&p, &n, &ti)) goto bailout; if (!(!ti.class && ti.tag == TAG_OCTET_STRING && ti.length == sizeof iv)) goto bailout; /* Bad IV. */ memcpy (iv, p, sizeof iv); p += sizeof iv; n -= sizeof iv; } else { where = "rc2or3des-params"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_OCTET_STRING || ti.length < 8 || ti.length > 20 ) goto bailout; saltlen = ti.length; memcpy (salt, p, saltlen); p += saltlen; n -= saltlen; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_INTEGER || !ti.length ) goto bailout; for (iter=0; ti.length; ti.length--) { iter <<= 8; iter |= (*p++) & 0xff; n--; } } where = "rc2or3desoraes-ciphertext"; if (parse_tag (&p, &n, &ti)) goto bailout; consumed = p - p_start; if (ti.class == ASNCONTEXT && ti.tag == 0 && ti.is_constructed && ti.ndef) { /* Mozilla exported certs now come with single byte chunks of octet strings. (Mozilla Firefox 1.0.4). Arghh. */ where = "cram-rc2or3des-ciphertext"; cram_buffer = cram_octet_string ( p, &n, &consumed); if (!cram_buffer) goto bailout; p = p_start = cram_buffer; if (r_consumed) *r_consumed = consumed; r_consumed = NULL; /* Ugly hack to not update that value any further. */ ti.length = n; } else if (ti.class == ASNCONTEXT && ti.tag == 0 && ti.length ) ; else goto bailout; log_info ("%lu bytes of %s encrypted text\n",ti.length, is_pbes2?"AES128":is_3des?"3DES":"RC2"); plain = gcry_malloc_secure (ti.length); if (!plain) { log_error ("error allocating decryption buffer\n"); goto bailout; } decrypt_block (p, plain, ti.length, salt, saltlen, iter, iv, is_pbes2?16:0, pw, is_pbes2 ? GCRY_CIPHER_AES128 : is_3des ? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40, bag_decrypted_data_p); n = ti.length; startoffset = 0; p_start = p = plain; where = "outer.outer.seq"; if (parse_tag (&p, &n, &ti)) { bad_pass = 1; goto bailout; } if (ti.class || ti.tag != TAG_SEQUENCE) { bad_pass = 1; goto bailout; } if (parse_tag (&p, &n, &ti)) { bad_pass = 1; goto bailout; } /* Loop over all certificates inside the bag. */ while (n) { int iscrlbag = 0; int iskeybag = 0; where = "certbag.nextcert"; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; where = "certbag.objectidentifier"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_OBJECT_ID) goto bailout; if ( ti.length == DIM(oid_pkcs_12_CertBag) && !memcmp (p, oid_pkcs_12_CertBag, DIM(oid_pkcs_12_CertBag))) { p += DIM(oid_pkcs_12_CertBag); n -= DIM(oid_pkcs_12_CertBag); } else if ( ti.length == DIM(oid_pkcs_12_CrlBag) && !memcmp (p, oid_pkcs_12_CrlBag, DIM(oid_pkcs_12_CrlBag))) { p += DIM(oid_pkcs_12_CrlBag); n -= DIM(oid_pkcs_12_CrlBag); 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; where = "certbag.before.certheader"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class != ASNCONTEXT || ti.tag) goto bailout; 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) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_x509Certificate_for_pkcs_12) || memcmp (p, oid_x509Certificate_for_pkcs_12, DIM(oid_x509Certificate_for_pkcs_12))) goto bailout; p += DIM(oid_x509Certificate_for_pkcs_12); n -= DIM(oid_x509Certificate_for_pkcs_12); where = "certbag.before.octetstring"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class != ASNCONTEXT || ti.tag) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_OCTET_STRING || ti.ndef) goto bailout; /* Return the certificate. */ if (certcb) certcb (certcbarg, p, ti.length); p += ti.length; n -= ti.length; } /* Ugly hack to cope with the padding: Forget about the rest if that is less or equal to the cipher's block length. We can reasonable assume that all valid data will be longer than just one block. */ if (n <= (is_pbes2? 16:8)) n = 0; /* Skip the optional SET with the pkcs12 cert attributes. */ if (n) { where = "bag.attributes"; if (parse_tag (&p, &n, &ti)) goto bailout; if (!ti.class && ti.tag == TAG_SEQUENCE) ; /* No attributes. */ else if (!ti.class && ti.tag == TAG_SET && !ti.ndef) { /* The optional SET. */ p += ti.length; n -= ti.length; if (n <= (is_pbes2?16:8)) n = 0; if (n && parse_tag (&p, &n, &ti)) goto bailout; } else goto bailout; } } if (r_consumed) *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); gcry_free (cram_buffer); log_error ("encryptedData error at \"%s\", offset %u\n", where, (unsigned int)((p - p_start)+startoffset)); if (bad_pass) { /* Note, that the following string might be used by other programs to check for a bad passphrase; it should therefore not be translated or changed. */ log_error ("possibly bad passphrase given\n"); *r_badpass = 1; } 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"); */ +/* FILE *fp = fopen ("tmp-minip12-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) + size_t *r_consumed, char **r_curve, const char *pw) { int rc; struct tag_info ti; const unsigned char *p = buffer; const unsigned char *p_start = buffer; size_t n = length; const char *where; char salt[20]; size_t saltlen; char iv[16]; unsigned int iter; int len; unsigned char *plain = NULL; gcry_mpi_t *result = NULL; int result_count, i; unsigned char *cram_buffer = NULL; size_t consumed = 0; /* Number of bytes consumed from the original buffer. */ int is_pbes2 = 0; + char *curve = NULL; where = "start"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class != ASNCONTEXT || ti.tag) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_OCTET_STRING) goto bailout; consumed = p - p_start; if (ti.is_constructed && ti.ndef) { /* Mozilla exported certs now come with single byte chunks of octet strings. (Mozilla Firefox 1.0.4). Arghh. */ where = "cram-data.outersegs"; cram_buffer = cram_octet_string ( p, &n, &consumed); if (!cram_buffer) goto bailout; p = p_start = cram_buffer; if (r_consumed) *r_consumed = consumed; r_consumed = NULL; /* Ugly hack to not update that value any further. */ } where = "data.outerseqs"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; where = "data.objectidentifier"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag) || memcmp (p, oid_pkcs_12_pkcs_8ShroudedKeyBag, DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag))) goto bailout; p += DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag); n -= DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag); where = "shrouded,outerseqs"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class != ASNCONTEXT || ti.tag) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class == 0 && 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); } else if (ti.class == 0 && ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_pkcs5PBES2) && !memcmp (p, oid_pkcs5PBES2, DIM(oid_pkcs5PBES2))) { p += DIM(oid_pkcs5PBES2); n -= DIM(oid_pkcs5PBES2); is_pbes2 = 1; } else goto bailout; if (is_pbes2) { where = "pkcs5PBES2-params"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (!(!ti.class && ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_pkcs5PBKDF2) && !memcmp (p, oid_pkcs5PBKDF2, ti.length))) goto bailout; /* Not PBKDF2. */ p += ti.length; n -= ti.length; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (!(!ti.class && ti.tag == TAG_OCTET_STRING && ti.length >= 8 && ti.length < sizeof salt)) goto bailout; /* No salt or unsupported length. */ saltlen = ti.length; memcpy (salt, p, saltlen); p += saltlen; n -= saltlen; if (parse_tag (&p, &n, &ti)) goto bailout; if (!(!ti.class && ti.tag == TAG_INTEGER && ti.length)) goto bailout; /* No valid iteration count. */ for (iter=0; ti.length; ti.length--) { iter <<= 8; iter |= (*p++) & 0xff; n--; } /* Note: We don't support the optional parameters but assume that the algorithmIdentifier follows. */ if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (!(!ti.class && ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_aes128_CBC) && !memcmp (p, oid_aes128_CBC, ti.length))) goto bailout; /* Not AES-128. */ p += ti.length; n -= ti.length; if (parse_tag (&p, &n, &ti)) goto bailout; if (!(!ti.class && ti.tag == TAG_OCTET_STRING && ti.length == sizeof iv)) goto bailout; /* Bad IV. */ memcpy (iv, p, sizeof iv); p += sizeof iv; n -= sizeof iv; } else { where = "3des-params"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_OCTET_STRING || ti.length < 8 || ti.length > 20) goto bailout; saltlen = ti.length; memcpy (salt, p, saltlen); p += saltlen; n -= saltlen; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_INTEGER || !ti.length ) goto bailout; for (iter=0; ti.length; ti.length--) { iter <<= 8; iter |= (*p++) & 0xff; n--; } } where = "3desoraes-ciphertext"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_OCTET_STRING || !ti.length ) goto bailout; log_info ("%lu bytes of %s encrypted text\n", ti.length, is_pbes2? "AES128":"3DES"); plain = gcry_malloc_secure (ti.length); if (!plain) { log_error ("error allocating decryption buffer\n"); goto bailout; } consumed += p - p_start + ti.length; decrypt_block (p, plain, ti.length, salt, saltlen, iter, iv, is_pbes2? 16:0, pw, is_pbes2? GCRY_CIPHER_AES128 : GCRY_CIPHER_3DES, bag_data_p); n = ti.length; startoffset = 0; p_start = p = plain; where = "decrypted-text"; 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))) + if (ti.class || ti.tag != TAG_OBJECT_ID) + goto bailout; + /* gpgrt_log_printhex (p, ti.length, "OID:"); */ + if (ti.length == DIM(oid_rsaEncryption) + && !memcmp (p, oid_rsaEncryption, DIM(oid_rsaEncryption))) + { + p += DIM (oid_rsaEncryption); + n -= DIM (oid_rsaEncryption); + } + else if (ti.length == DIM(oid_pcPublicKey) + && !memcmp (p, oid_pcPublicKey, DIM(oid_pcPublicKey))) + { + /* See RFC-5915 for the format. */ + p += DIM (oid_pcPublicKey); + n -= DIM (oid_pcPublicKey); + if (len < ti.length) + goto bailout; + len -= ti.length; + if (n < len) + goto bailout; + if (parse_tag (&p, &n, &ti)) + goto bailout; + /* gpgrt_log_debug ("ti=%d/%lu len=%lu\n",ti.class,ti.tag,ti.length); */ + if (len < ti.nhdr) + goto bailout; + len -= ti.nhdr; + if (ti.class || ti.tag != TAG_OBJECT_ID) + goto bailout; + curve = ksba_oid_to_str (p, ti.length); + if (!curve) + goto bailout; + /* log_debug ("OID of curve is: %s\n", curve); */ + p += ti.length; + n -= ti.length; + } + else 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.key-parameters"; - for (result_count=0; len && result_count < 9;) + if (curve) /* ECC case. */ { 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 + if (ti.length != 1 && *p != 1) { - 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++; + log_error ("error parsing private ecPublicKey parameter: %s\n", + "bad version"); + goto bailout; } p += ti.length; n -= ti.length; + if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_OCTET_STRING) + goto bailout; + if (len < ti.nhdr) + goto bailout; + len -= ti.nhdr; + if (len < ti.length) + goto bailout; + len -= ti.length; + /* log_printhex (p, ti.length, "ecc q="); */ + rc = gcry_mpi_scan (result, GCRYMPI_FMT_USG, p, ti.length, NULL); + if (rc) + { + log_error ("error parsing key parameter: %s\n", gpg_strerror (rc)); + goto bailout; + } + p += ti.length; + n -= ti.length; + + len = 0; /* Skip the rest. */ + } + else /* RSA case */ + { + 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 + { + 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; - gcry_free (cram_buffer); - if (r_consumed) - *r_consumed = consumed; - return result; + goto leave; bailout: gcry_free (plain); if (result) { for (i=0; result[i]; i++) gcry_mpi_release (result[i]); gcry_free (result); } - gcry_free (cram_buffer); log_error ( "data error at \"%s\", offset %u\n", where, (unsigned int)((p - buffer) + startoffset)); + result = NULL; + + leave: + if (r_curve && result) + { + *r_curve = curve; + curve = NULL; + } + else if (r_curve) + *r_curve = NULL; + ksba_free (curve); + gcry_free (cram_buffer); if (r_consumed) *r_consumed = consumed; - return NULL; + return result; } /* Parse a PKCS12 object and return an array of MPI representing the secret key parameters. This is a very limited implementation in that it is only able to look for 3DES encoded encryptedData and tries to extract the first private key object it finds. In case of an error NULL is returned. CERTCB and CERRTCBARG are used to pass X.509 certificates back to the caller. */ gcry_mpi_t * p12_parse (const unsigned char *buffer, size_t length, const char *pw, void (*certcb)(void*, const unsigned char*, size_t), - void *certcbarg, int *r_badpass) + void *certcbarg, int *r_badpass, char **r_curve) { struct tag_info ti; const unsigned char *p = buffer; const unsigned char *p_start = buffer; size_t n = length; const char *where; int bagseqlength, len; int bagseqndef, lenndef; gcry_mpi_t *result = NULL; unsigned char *cram_buffer = NULL; + char *curve = NULL; *r_badpass = 0; where = "pfx"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.tag != TAG_SEQUENCE) goto bailout; where = "pfxVersion"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 3) goto bailout; p++; n--; where = "authSave"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_data) || memcmp (p, oid_data, DIM(oid_data))) goto bailout; p += DIM(oid_data); n -= DIM(oid_data); if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class != ASNCONTEXT || ti.tag) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class != UNIVERSAL || ti.tag != TAG_OCTET_STRING) goto bailout; if (ti.is_constructed && ti.ndef) { /* Mozilla exported certs now come with single byte chunks of octet strings. (Mozilla Firefox 1.0.4). Arghh. */ where = "cram-bags"; cram_buffer = cram_octet_string ( p, &n, NULL); if (!cram_buffer) goto bailout; p = p_start = cram_buffer; } where = "bags"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class != UNIVERSAL || ti.tag != TAG_SEQUENCE) goto bailout; bagseqndef = ti.ndef; bagseqlength = ti.length; while (bagseqlength || bagseqndef) { /* log_debug ( "at offset %u\n", (p - p_start)); */ where = "bag-sequence"; if (parse_tag (&p, &n, &ti)) goto bailout; if (bagseqndef && ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed) break; /* Ready */ if (ti.class != UNIVERSAL || ti.tag != TAG_SEQUENCE) goto bailout; if (!bagseqndef) { if (bagseqlength < ti.nhdr) goto bailout; bagseqlength -= ti.nhdr; if (bagseqlength < ti.length) goto bailout; bagseqlength -= ti.length; } lenndef = ti.ndef; len = ti.length; if (parse_tag (&p, &n, &ti)) goto bailout; if (lenndef) len = ti.nhdr; else len -= ti.nhdr; if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_encryptedData) && !memcmp (p, oid_encryptedData, DIM(oid_encryptedData))) { size_t consumed = 0; p += DIM(oid_encryptedData); n -= DIM(oid_encryptedData); if (!lenndef) len -= DIM(oid_encryptedData); where = "bag.encryptedData"; if (parse_bag_encrypted_data (p, n, (p - p_start), &consumed, pw, certcb, certcbarg, result? NULL : &result, r_badpass)) goto bailout; if (lenndef) len += consumed; } else if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_data) && !memcmp (p, oid_data, DIM(oid_data))) { if (result) { log_info ("already got an key object, skipping this one\n"); p += ti.length; n -= ti.length; } else { size_t consumed = 0; p += DIM(oid_data); n -= DIM(oid_data); if (!lenndef) len -= DIM(oid_data); - result = parse_bag_data (p, n, (p - p_start), &consumed, pw); + result = parse_bag_data (p, n, (p - p_start), + &consumed, &curve, pw); if (!result) goto bailout; if (lenndef) len += consumed; } } else { log_info ("unknown bag type - skipped\n"); p += ti.length; n -= ti.length; } if (len < 0 || len > n) goto bailout; p += len; n -= len; if (lenndef) { /* Need to skip the Null Tag. */ if (parse_tag (&p, &n, &ti)) goto bailout; if (!(ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed)) goto bailout; } } gcry_free (cram_buffer); + *r_curve = curve; return result; + bailout: log_error ("error at \"%s\", offset %u\n", where, (unsigned int)(p - p_start)); if (result) { int i; for (i=0; result[i]; i++) gcry_mpi_release (result[i]); gcry_free (result); } gcry_free (cram_buffer); + gcry_free (curve); + *r_curve = NULL; return NULL; } static size_t compute_tag_length (size_t n) { int needed = 0; if (n < 128) needed += 2; /* tag and one length byte */ else if (n < 256) needed += 3; /* tag, number of length bytes, 1 length byte */ else if (n < 65536) needed += 4; /* tag, number of length bytes, 2 length bytes */ else { log_error ("object too larger to encode\n"); return 0; } return needed; } static unsigned char * store_tag_length (unsigned char *p, int tag, size_t n) { if (tag == TAG_SEQUENCE) tag |= 0x20; /* constructed */ *p++ = tag; if (n < 128) *p++ = n; else if (n < 256) { *p++ = 0x81; *p++ = n; } else if (n < 65536) { *p++ = 0x82; *p++ = n >> 8; *p++ = n; } return p; } /* Create the final PKCS-12 object from the sequences contained in SEQLIST. PW is the password. That array is terminated with an NULL object. */ static unsigned char * create_final (struct buffer_s *sequences, const char *pw, size_t *r_length) { int i; size_t needed = 0; size_t len[8], n; unsigned char *macstart; size_t maclen; unsigned char *result, *p; size_t resultlen; char salt[8]; unsigned char keybuf[20]; gcry_md_hd_t md; int rc; int with_mac = 1; /* 9 steps to create the pkcs#12 Krampf. */ /* 8. The MAC. */ /* We add this at step 0. */ /* 7. All the buffers. */ for (i=0; sequences[i].buffer; i++) needed += sequences[i].length; /* 6. This goes into a sequences. */ len[6] = needed; n = compute_tag_length (needed); needed += n; /* 5. Encapsulate all in an octet string. */ len[5] = needed; n = compute_tag_length (needed); needed += n; /* 4. And tag it with [0]. */ len[4] = needed; n = compute_tag_length (needed); needed += n; /* 3. Prepend an data OID. */ needed += 2 + DIM (oid_data); /* 2. Put all into a sequences. */ len[2] = needed; n = compute_tag_length (needed); needed += n; /* 1. Prepend the version integer 3. */ needed += 3; /* 0. And the final outer sequence. */ if (with_mac) needed += DIM (data_mactemplate); len[0] = needed; n = compute_tag_length (needed); needed += n; /* Allocate a buffer. */ result = gcry_malloc (needed); if (!result) { log_error ("error allocating buffer\n"); return NULL; } p = result; /* 0. Store the very outer sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[0]); /* 1. Store the version integer 3. */ *p++ = TAG_INTEGER; *p++ = 1; *p++ = 3; /* 2. Store another sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[2]); /* 3. Store the data OID. */ p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data)); memcpy (p, oid_data, DIM (oid_data)); p += DIM (oid_data); /* 4. Next comes a context tag. */ p = store_tag_length (p, 0xa0, len[4]); /* 5. And an octet string. */ p = store_tag_length (p, TAG_OCTET_STRING, len[5]); /* 6. And the inner sequence. */ macstart = p; p = store_tag_length (p, TAG_SEQUENCE, len[6]); /* 7. Append all the buffers. */ for (i=0; sequences[i].buffer; i++) { memcpy (p, sequences[i].buffer, sequences[i].length); p += sequences[i].length; } if (with_mac) { /* 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); } /* Ready. */ resultlen = p - result; if (needed != resultlen) log_debug ("length mismatch: %lu, %lu\n", (unsigned long)needed, (unsigned long)resultlen); *r_length = resultlen; return result; } /* Build a DER encoded SEQUENCE with the key: SEQUENCE { INTEGER 0 SEQUENCE { OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1) NULL } OCTET STRING, encapsulates { SEQUENCE { INTEGER 0 INTEGER INTEGER INTEGER INTEGER INTEGER INTEGER INTEGER INTEGER } } } MODE controls what is being generated: 0 - As described above 1 - Ditto but without the padding 2 - Only the inner part (pkcs#1) */ static unsigned char * build_key_sequence (gcry_mpi_t *kparms, int mode, size_t *r_length) { int rc, i; size_t needed, n; unsigned char *plain, *p; size_t plainlen; size_t outseqlen, oidseqlen, octstrlen, inseqlen; needed = 3; /* The version integer with value 0. */ for (i=0; kparms[i]; i++) { n = 0; rc = gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &n, kparms[i]); if (rc) { log_error ("error formatting parameter: %s\n", gpg_strerror (rc)); return NULL; } needed += n; n = compute_tag_length (n); if (!n) return NULL; needed += n; } if (i != 8) { log_error ("invalid parameters for p12_build\n"); return NULL; } /* Now this all goes into a sequence. */ inseqlen = needed; n = compute_tag_length (needed); if (!n) return NULL; needed += n; if (mode != 2) { /* Encapsulate all into an octet string. */ octstrlen = needed; n = compute_tag_length (needed); if (!n) return NULL; needed += n; /* Prepend the object identifier sequence. */ oidseqlen = 2 + DIM (oid_rsaEncryption) + 2; needed += 2 + oidseqlen; /* The version number. */ needed += 3; /* And finally put the whole thing into a sequence. */ outseqlen = needed; n = compute_tag_length (needed); if (!n) return NULL; needed += n; } /* allocate 8 extra bytes for padding */ plain = gcry_malloc_secure (needed+8); if (!plain) { log_error ("error allocating encryption buffer\n"); return NULL; } /* And now fill the plaintext buffer. */ p = plain; if (mode != 2) { p = store_tag_length (p, TAG_SEQUENCE, outseqlen); /* Store version. */ *p++ = TAG_INTEGER; *p++ = 1; *p++ = 0; /* Store object identifier sequence. */ p = store_tag_length (p, TAG_SEQUENCE, oidseqlen); p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_rsaEncryption)); memcpy (p, oid_rsaEncryption, DIM (oid_rsaEncryption)); p += DIM (oid_rsaEncryption); *p++ = TAG_NULL; *p++ = 0; /* Start with the octet string. */ p = store_tag_length (p, TAG_OCTET_STRING, octstrlen); } p = store_tag_length (p, TAG_SEQUENCE, inseqlen); /* Store the key parameters. */ *p++ = TAG_INTEGER; *p++ = 1; *p++ = 0; for (i=0; kparms[i]; i++) { n = 0; rc = gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &n, kparms[i]); if (rc) { log_error ("oops: error formatting parameter: %s\n", gpg_strerror (rc)); gcry_free (plain); return NULL; } p = store_tag_length (p, TAG_INTEGER, n); n = plain + needed - p; rc = gcry_mpi_print (GCRYMPI_FMT_STD, p, n, &n, kparms[i]); if (rc) { log_error ("oops: error storing parameter: %s\n", gpg_strerror (rc)); gcry_free (plain); return NULL; } p += n; } plainlen = p - plain; assert (needed == plainlen); if (!mode) { /* Append some pad characters; we already allocated extra space. */ n = 8 - plainlen % 8; for (i=0; i < n; i++, plainlen++) *p++ = n; } *r_length = plainlen; return plain; } static unsigned char * build_key_bag (unsigned char *buffer, size_t buflen, char *salt, const unsigned char *sha1hash, const char *keyidstr, size_t *r_length) { size_t len[11], needed; unsigned char *p, *keybag; size_t keybaglen; /* Walk 11 steps down to collect the info: */ /* 10. The data goes into an octet string. */ needed = compute_tag_length (buflen); needed += buflen; /* 9. Prepend the algorithm identifier. */ needed += DIM (data_3desiter2048); /* 8. Put a sequence around. */ len[8] = needed; needed += compute_tag_length (needed); /* 7. Prepend a [0] tag. */ len[7] = needed; needed += compute_tag_length (needed); /* 6b. The attributes which are appended at the end. */ if (sha1hash) needed += DIM (data_attrtemplate) + 20; /* 6. Prepend the shroudedKeyBag OID. */ needed += 2 + DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag); /* 5+4. Put all into two sequences. */ len[5] = needed; needed += compute_tag_length ( needed); len[4] = needed; needed += compute_tag_length (needed); /* 3. This all goes into an octet string. */ len[3] = needed; needed += compute_tag_length (needed); /* 2. Prepend another [0] tag. */ len[2] = needed; needed += compute_tag_length (needed); /* 1. Prepend the data OID. */ needed += 2 + DIM (oid_data); /* 0. Prepend another sequence. */ len[0] = needed; needed += compute_tag_length (needed); /* Now that we have all length information, allocate a buffer. */ p = keybag = gcry_malloc (needed); if (!keybag) { log_error ("error allocating buffer\n"); return NULL; } /* Walk 11 steps up to store the data. */ /* 0. Store the first sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[0]); /* 1. Store the data OID. */ p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data)); memcpy (p, oid_data, DIM (oid_data)); p += DIM (oid_data); /* 2. Store a [0] tag. */ p = store_tag_length (p, 0xa0, len[2]); /* 3. And an octet string. */ p = store_tag_length (p, TAG_OCTET_STRING, len[3]); /* 4+5. Two sequences. */ p = store_tag_length (p, TAG_SEQUENCE, len[4]); p = store_tag_length (p, TAG_SEQUENCE, len[5]); /* 6. Store the shroudedKeyBag OID. */ p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag)); memcpy (p, oid_pkcs_12_pkcs_8ShroudedKeyBag, DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag)); p += DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag); /* 7. Store a [0] tag. */ p = store_tag_length (p, 0xa0, len[7]); /* 8. Store a sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[8]); /* 9. Now for the pre-encoded algorithm identifier and the salt. */ memcpy (p, data_3desiter2048, DIM (data_3desiter2048)); memcpy (p + DATA_3DESITER2048_SALT_OFF, salt, 8); p += DIM (data_3desiter2048); /* 10. And the octet string with the encrypted data. */ p = store_tag_length (p, TAG_OCTET_STRING, buflen); memcpy (p, buffer, buflen); p += buflen; /* Append the attributes whose length we calculated at step 2b. */ if (sha1hash) { int i; memcpy (p, data_attrtemplate, DIM (data_attrtemplate)); for (i=0; i < 8; i++) p[DATA_ATTRTEMPLATE_KEYID_OFF+2*i+1] = keyidstr[i]; p += DIM (data_attrtemplate); memcpy (p, sha1hash, 20); p += 20; } keybaglen = p - keybag; if (needed != keybaglen) log_debug ("length mismatch: %lu, %lu\n", (unsigned long)needed, (unsigned long)keybaglen); *r_length = keybaglen; return keybag; } static unsigned char * build_cert_bag (unsigned char *buffer, size_t buflen, char *salt, size_t *r_length) { size_t len[9], needed; unsigned char *p, *certbag; size_t certbaglen; /* Walk 9 steps down to collect the info: */ /* 8. The data goes into an octet string. */ needed = compute_tag_length (buflen); needed += buflen; /* 7. The algorithm identifier. */ needed += DIM (data_rc2iter2048); /* 6. The data OID. */ needed += 2 + DIM (oid_data); /* 5. A sequence. */ len[5] = needed; needed += compute_tag_length ( needed); /* 4. An integer. */ needed += 3; /* 3. A sequence. */ len[3] = needed; needed += compute_tag_length (needed); /* 2. A [0] tag. */ len[2] = needed; needed += compute_tag_length (needed); /* 1. The encryptedData OID. */ needed += 2 + DIM (oid_encryptedData); /* 0. The first sequence. */ len[0] = needed; needed += compute_tag_length (needed); /* Now that we have all length information, allocate a buffer. */ p = certbag = gcry_malloc (needed); if (!certbag) { log_error ("error allocating buffer\n"); return NULL; } /* Walk 9 steps up to store the data. */ /* 0. Store the first sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[0]); /* 1. Store the encryptedData OID. */ p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_encryptedData)); memcpy (p, oid_encryptedData, DIM (oid_encryptedData)); p += DIM (oid_encryptedData); /* 2. Store a [0] tag. */ p = store_tag_length (p, 0xa0, len[2]); /* 3. Store a sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[3]); /* 4. Store the integer 0. */ *p++ = TAG_INTEGER; *p++ = 1; *p++ = 0; /* 5. Store a sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[5]); /* 6. Store the data OID. */ p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data)); memcpy (p, oid_data, DIM (oid_data)); p += DIM (oid_data); /* 7. Now for the pre-encoded algorithm identifier and the salt. */ memcpy (p, data_rc2iter2048, DIM (data_rc2iter2048)); memcpy (p + DATA_RC2ITER2048_SALT_OFF, salt, 8); p += DIM (data_rc2iter2048); /* 8. And finally the [0] tag with the encrypted data. */ p = store_tag_length (p, 0x80, buflen); memcpy (p, buffer, buflen); p += buflen; certbaglen = p - certbag; if (needed != certbaglen) log_debug ("length mismatch: %lu, %lu\n", (unsigned long)needed, (unsigned long)certbaglen); *r_length = certbaglen; return certbag; } static unsigned char * build_cert_sequence (const unsigned char *buffer, size_t buflen, const unsigned char *sha1hash, const char *keyidstr, size_t *r_length) { size_t len[8], needed, n; unsigned char *p, *certseq; size_t certseqlen; int i; assert (strlen (keyidstr) == 8); /* Walk 8 steps down to collect the info: */ /* 7. The data goes into an octet string. */ needed = compute_tag_length (buflen); needed += buflen; /* 6. A [0] tag. */ len[6] = needed; needed += compute_tag_length (needed); /* 5. An OID. */ needed += 2 + DIM (oid_x509Certificate_for_pkcs_12); /* 4. A sequence. */ len[4] = needed; needed += compute_tag_length (needed); /* 3. A [0] tag. */ len[3] = needed; needed += compute_tag_length (needed); /* 2b. The attributes which are appended at the end. */ if (sha1hash) needed += DIM (data_attrtemplate) + 20; /* 2. An OID. */ needed += 2 + DIM (oid_pkcs_12_CertBag); /* 1. A sequence. */ len[1] = needed; needed += compute_tag_length (needed); /* 0. The first sequence. */ len[0] = needed; needed += compute_tag_length (needed); /* Now that we have all length information, allocate a buffer. */ p = certseq = gcry_malloc (needed + 8 /*(for padding)*/); if (!certseq) { log_error ("error allocating buffer\n"); return NULL; } /* Walk 8 steps up to store the data. */ /* 0. Store the first sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[0]); /* 1. Store the second sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[1]); /* 2. Store the pkcs12-cert-bag OID. */ p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_pkcs_12_CertBag)); memcpy (p, oid_pkcs_12_CertBag, DIM (oid_pkcs_12_CertBag)); p += DIM (oid_pkcs_12_CertBag); /* 3. Store a [0] tag. */ p = store_tag_length (p, 0xa0, len[3]); /* 4. Store a sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[4]); /* 5. Store the x509Certificate OID. */ p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_x509Certificate_for_pkcs_12)); memcpy (p, oid_x509Certificate_for_pkcs_12, DIM (oid_x509Certificate_for_pkcs_12)); p += DIM (oid_x509Certificate_for_pkcs_12); /* 6. Store a [0] tag. */ p = store_tag_length (p, 0xa0, len[6]); /* 7. And the octet string with the actual certificate. */ p = store_tag_length (p, TAG_OCTET_STRING, buflen); memcpy (p, buffer, buflen); p += buflen; /* Append the attributes whose length we calculated at step 2b. */ if (sha1hash) { memcpy (p, data_attrtemplate, DIM (data_attrtemplate)); for (i=0; i < 8; i++) p[DATA_ATTRTEMPLATE_KEYID_OFF+2*i+1] = keyidstr[i]; p += DIM (data_attrtemplate); memcpy (p, sha1hash, 20); p += 20; } certseqlen = p - certseq; if (needed != certseqlen) log_debug ("length mismatch: %lu, %lu\n", (unsigned long)needed, (unsigned long)certseqlen); /* Append some pad characters; we already allocated extra space. */ n = 8 - certseqlen % 8; for (i=0; i < n; i++, certseqlen++) *p++ = n; *r_length = certseqlen; return certseq; } /* Expect the RSA key parameters in KPARMS and a password in PW. Create a PKCS structure from it and return it as well as the length in R_LENGTH; return NULL in case of an error. If CHARSET is not NULL, re-encode PW to that character set. */ unsigned char * p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen, const char *pw, const char *charset, size_t *r_length) { unsigned char *buffer = NULL; size_t n, buflen; char salt[8]; struct buffer_s seqlist[3]; int seqlistidx = 0; unsigned char sha1hash[20]; char keyidstr[8+1]; char *pwbuf = NULL; size_t pwbufsize = 0; n = buflen = 0; /* (avoid compiler warning). */ memset (sha1hash, 0, 20); *keyidstr = 0; if (charset && pw && *pw) { jnlib_iconv_t cd; const char *inptr; char *outptr; size_t inbytes, outbytes; /* We assume that the converted passphrase is at max 2 times longer than its utf-8 encoding. */ pwbufsize = strlen (pw)*2 + 1; pwbuf = gcry_malloc_secure (pwbufsize); if (!pwbuf) { log_error ("out of secure memory while converting passphrase\n"); goto failure; } cd = jnlib_iconv_open (charset, "utf-8"); if (cd == (jnlib_iconv_t)(-1)) { log_error ("can't convert passphrase to" " requested charset '%s': %s\n", charset, strerror (errno)); goto failure; } inptr = pw; inbytes = strlen (pw); outptr = pwbuf; outbytes = pwbufsize - 1; if ( jnlib_iconv (cd, (const char **)&inptr, &inbytes, &outptr, &outbytes) == (size_t)-1) { log_error ("error converting passphrase to" " requested charset '%s': %s\n", charset, strerror (errno)); jnlib_iconv_close (cd); goto failure; } *outptr = 0; jnlib_iconv_close (cd); pw = pwbuf; } if (cert && certlen) { /* Calculate the hash value we need for the bag attributes. */ gcry_md_hash_buffer (GCRY_MD_SHA1, sha1hash, cert, certlen); sprintf (keyidstr, "%02x%02x%02x%02x", sha1hash[16], sha1hash[17], sha1hash[18], sha1hash[19]); /* Encode the certificate. */ buffer = build_cert_sequence (cert, certlen, sha1hash, keyidstr, &buflen); if (!buffer) goto failure; /* Encrypt it. */ gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); crypt_block (buffer, buflen, salt, 8, 2048, NULL, 0, pw, GCRY_CIPHER_RFC2268_40, 1); /* Encode the encrypted stuff into a bag. */ seqlist[seqlistidx].buffer = build_cert_bag (buffer, buflen, salt, &n); seqlist[seqlistidx].length = n; gcry_free (buffer); buffer = NULL; if (!seqlist[seqlistidx].buffer) goto failure; seqlistidx++; } if (kparms) { /* Encode the key. */ buffer = build_key_sequence (kparms, 0, &buflen); if (!buffer) goto failure; /* Encrypt it. */ gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); crypt_block (buffer, buflen, salt, 8, 2048, NULL, 0, pw, GCRY_CIPHER_3DES, 1); /* Encode the encrypted stuff into a bag. */ if (cert && certlen) seqlist[seqlistidx].buffer = build_key_bag (buffer, buflen, salt, sha1hash, keyidstr, &n); else seqlist[seqlistidx].buffer = build_key_bag (buffer, buflen, salt, NULL, NULL, &n); seqlist[seqlistidx].length = n; gcry_free (buffer); buffer = NULL; if (!seqlist[seqlistidx].buffer) goto failure; seqlistidx++; } seqlist[seqlistidx].buffer = NULL; seqlist[seqlistidx].length = 0; buffer = create_final (seqlist, pw, &buflen); failure: if (pwbuf) { /* Note that wipememory is not really needed due to the use of gcry_malloc_secure. */ wipememory (pwbuf, pwbufsize); gcry_free (pwbuf); } for ( ; seqlistidx; seqlistidx--) gcry_free (seqlist[seqlistidx].buffer); *r_length = buffer? buflen : 0; return buffer; } /* This is actually not a pkcs#12 function but one which creates an unencrypted a pkcs#1 private key. */ unsigned char * p12_raw_build (gcry_mpi_t *kparms, int rawmode, size_t *r_length) { unsigned char *buffer; size_t buflen; assert (rawmode == 1 || rawmode == 2); buffer = build_key_sequence (kparms, rawmode, &buflen); if (!buffer) return NULL; *r_length = buflen; return buffer; } #ifdef TEST static void cert_cb (void *opaque, const unsigned char *cert, size_t certlen) { printf ("got a certificate of %u bytes length\n", certlen); } int main (int argc, char **argv) { FILE *fp; struct stat st; unsigned char *buf; size_t buflen; gcry_mpi_t *result; int badpass; if (argc != 3) { fprintf (stderr, "usage: testp12 file passphrase\n"); return 1; } gcry_control (GCRYCTL_DISABLE_SECMEM, NULL); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, NULL); fp = fopen (argv[1], "rb"); if (!fp) { fprintf (stderr, "can't open '%s': %s\n", argv[1], strerror (errno)); return 1; } if (fstat (fileno(fp), &st)) { fprintf (stderr, "can't stat '%s': %s\n", argv[1], strerror (errno)); return 1; } buflen = st.st_size; buf = gcry_malloc (buflen+1); if (!buf || fread (buf, buflen, 1, fp) != 1) { fprintf (stderr, "error reading '%s': %s\n", argv[1], strerror (errno)); return 1; } fclose (fp); result = p12_parse (buf, buflen, argv[2], cert_cb, NULL, &badpass); if (result) { int i, rc; unsigned char *tmpbuf; for (i=0; result[i]; i++) { rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &tmpbuf, NULL, result[i]); if (rc) printf ("%d: [error printing number: %s]\n", i, gpg_strerror (rc)); else { printf ("%d: %s\n", i, tmpbuf); gcry_free (tmpbuf); } } } return 0; } /* Local Variables: compile-command: "gcc -Wall -O0 -g -DTEST=1 -o minip12 minip12.c ../common/libcommon.a -L /usr/local/lib -lgcrypt -lgpg-error" End: */ #endif /* TEST */ diff --git a/sm/minip12.h b/sm/minip12.h index 39a81939d..f4fba65ff 100644 --- a/sm/minip12.h +++ b/sm/minip12.h @@ -1,39 +1,39 @@ /* minip12.h - Global definitions for the minimal pkcs-12 implementation. * Copyright (C) 2002, 2003 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 3 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, see . */ #ifndef MINIP12_H #define MINIP12_H #include gcry_mpi_t *p12_parse (const unsigned char *buffer, size_t length, const char *pw, void (*certcb)(void*, const unsigned char*, size_t), - void *certcbarg, int *r_badpass); + void *certcbarg, int *r_badpass, char **r_curve); unsigned char *p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen, const char *pw, const char *charset, size_t *r_length); unsigned char *p12_raw_build (gcry_mpi_t *kparms, int rawmode, size_t *r_length); #endif /*MINIP12_H*/