From 0cb622d632f732c24a5d312baf2c6e453775eb10 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 15 Jan 2024 09:55:55 +0100 Subject: [PATCH] gpgsm: Allow parsing of PKCS#12 files with two private keys. * sm/minip12.c (struct p12_parse_ctx_s): Add privatekey2. (parse_shrouded_key_bag): Handle a second private key. (p12_parse_free_kparms): New. * sm/import.c (parse_p12): Factor some code out to ... (p12_to_skey): this. (parse_p12): Use p12_parse_free_kparms. -- Take care: We allow parsing of a second private key but we are not yet able to import the second private key. The whole things is required to at least import the certificates of current pkcs#12 files as created by the German Elster tax system. No test data, sorry. --- sm/import.c | 157 +++++++++++++++++++++++++------------------------ sm/minip12.c | 58 +++++++++++++++--- sm/minip12.h | 1 + sm/t-minip12.c | 8 +-- 4 files changed, 133 insertions(+), 91 deletions(-) diff --git a/sm/import.c b/sm/import.c index 5a193ef52..cbf8fa627 100644 --- a/sm/import.c +++ b/sm/import.c @@ -692,6 +692,85 @@ store_cert_cb (void *opaque, } +/* Helper for parse_p12. */ +static gpg_error_t +p12_to_skey (gcry_mpi_t *kparms, const char *curve, gcry_sexp_t *r_skey) +{ + gpg_error_t err = 0; + struct rsa_secret_key_s sk; + gcry_ctx_t ecctx = NULL; + + if (curve) + { + /* 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)); + 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); + goto leave; + } + + err = gcry_sexp_build (r_skey, 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 (r_skey, 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); + } + + leave: + gcry_ctx_release (ecctx); + return err; +} + + /* 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. */ @@ -706,7 +785,6 @@ parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats) 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; @@ -792,82 +870,9 @@ parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats) goto leave; } - 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); + err = p12_to_skey (kparms, curve, &s_key); + p12_parse_free_kparms (kparms); kparms = NULL; if (err) { diff --git a/sm/minip12.c b/sm/minip12.c index 1bbe126ae..2e7b50e1c 100644 --- a/sm/minip12.c +++ b/sm/minip12.c @@ -168,6 +168,9 @@ struct p12_parse_ctx_s /* The private key as an MPI array. */ gcry_mpi_t *privatekey; + + /* A second private key as an MPI array. */ + gcry_mpi_t *privatekey2; }; @@ -1248,6 +1251,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) int is_pbes2 = 0; int is_aes256 = 0; int digest_algo = GCRY_MD_SHA1; + gcry_mpi_t *privatekey; where = "shrouded_key_bag"; if (opt_verbose) @@ -1565,19 +1569,26 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (tlv_expect_sequence (tlv)) goto bailout; - if (ctx->privatekey) + if (ctx->privatekey2) { err = gpg_error (GPG_ERR_DUP_VALUE); - log_error ("a private key has already been received\n"); + log_error ("two private kesy have already been received\n"); goto bailout; } - ctx->privatekey = gcry_calloc (10, sizeof *ctx->privatekey); - if (!ctx->privatekey) + privatekey = gcry_calloc (10, sizeof *privatekey); + if (!privatekey) { err = gpg_error_from_syserror (); log_error ("error allocating privatekey element array\n"); goto bailout; } + if (ctx->privatekey) + { + log_info ("a private key has already been received - reading second\n"); + ctx->privatekey2 = privatekey; + } + else + ctx->privatekey = privatekey; where = "shrouded_key_bag.reading.key-parameters"; if (ctx->curve) /* ECC case. */ @@ -1600,7 +1611,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) goto bailout; if (opt_verbose > 1) log_printhex (data, datalen, "ecc q="); - err = gcry_mpi_scan (ctx->privatekey, GCRYMPI_FMT_USG, + err = gcry_mpi_scan (privatekey, GCRYMPI_FMT_USG, data, datalen, NULL); if (err) { @@ -1623,7 +1634,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) } err = tlv_expect_mpinteger (tlv, firstparam, - ctx->privatekey+keyelem_count); + privatekey+keyelem_count); if (firstparam && gpg_err_code (err) == GPG_ERR_FALSE) ; /* Ignore the first value iff it is zero. */ else if (err) @@ -1918,6 +1929,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, size_t oidlen; int intval; unsigned int startlevel; + int i; *r_badpass = 0; @@ -2037,6 +2049,15 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, else gcry_free (ctx.curve); + /* We have no way yet to return the second private key. */ + if (ctx.privatekey2) + { + for (i=0; ctx.privatekey2[i]; i++) + gcry_mpi_release (ctx.privatekey2[i]); + gcry_free (ctx.privatekey2); + ctx.privatekey2 = NULL; + } + return ctx.privatekey; bailout: @@ -2050,13 +2071,18 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, gpg_strerror (err)); if (ctx.privatekey) { - int i; - for (i=0; ctx.privatekey[i]; i++) gcry_mpi_release (ctx.privatekey[i]); gcry_free (ctx.privatekey); ctx.privatekey = NULL; } + if (ctx.privatekey2) + { + for (i=0; ctx.privatekey2[i]; i++) + gcry_mpi_release (ctx.privatekey2[i]); + gcry_free (ctx.privatekey2); + ctx.privatekey2 = NULL; + } tlv_parser_release (tlv); gcry_free (ctx.curve); if (r_curve) @@ -2065,6 +2091,22 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, } +/* Free the parameters as returned by p12_parse. */ +void +p12_parse_free_kparms (gcry_mpi_t *kparms) +{ + int i; + + if (kparms) + { + for (i=0; i < 8; i++) + gcry_mpi_release (kparms[i]); + gcry_free (kparms); + } +} + + + static size_t compute_tag_length (size_t n) diff --git a/sm/minip12.h b/sm/minip12.h index 654cab0e6..00569cd86 100644 --- a/sm/minip12.h +++ b/sm/minip12.h @@ -29,6 +29,7 @@ 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, char **r_curve); +void p12_parse_free_kparms (gcry_mpi_t *kparms); unsigned char *p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen, diff --git a/sm/t-minip12.c b/sm/t-minip12.c index bf3177ea0..75c1545d6 100644 --- a/sm/t-minip12.c +++ b/sm/t-minip12.c @@ -580,13 +580,7 @@ run_one_test (const char *name, const char *desc, const char *pass, ret = 0; } - if (result) - { - int i; - for (i=0; result[i]; i++) - gcry_mpi_release (result[i]); - gcry_free (result); - } + p12_parse_free_kparms (result); xfree (certstr); xfree (resulthash); xfree (curve);