diff options
author | Werner Koch <[email protected]> | 2020-04-09 10:18:08 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2020-04-09 10:20:19 +0000 |
commit | b45ab0ca08f8d6f9831192210b9ab141f4e450cf (patch) | |
tree | 114b0ef5f8e004c4a7c679decfd68af4ebf5708f /sm/certcheck.c | |
parent | common: New function to map hash algo names. (diff) | |
download | gnupg-b45ab0ca08f8d6f9831192210b9ab141f4e450cf.tar.gz gnupg-b45ab0ca08f8d6f9831192210b9ab141f4e450cf.zip |
sm,dirmngr: Support rsaPSS signature verification.
* sm/certcheck.c (hash_algo_from_buffer): New.
(uint_from_buffer): New.
(gpgsm_check_cert_sig): Handle PSS.
* dirmngr/crlcache.c (hash_algo_from_buffer): New.
(uint_from_buffer): New.
(start_sig_check): Detect PSS and extract hash algo. New arg to
return a PSS flag.
(finish_sig_check): New arg use_pss. Extract PSS args and use them.
(crl_parse_insert): Pass use_pss flag along.
--
GnuPG-bug-id: 4538
Signed-off-by: Werner Koch <[email protected]>
Diffstat (limited to 'sm/certcheck.c')
-rw-r--r-- | sm/certcheck.c | 190 |
1 files changed, 145 insertions, 45 deletions
diff --git a/sm/certcheck.c b/sm/certcheck.c index 51f1a9402..521f775ea 100644 --- a/sm/certcheck.c +++ b/sm/certcheck.c @@ -1,5 +1,7 @@ /* certcheck.c - check one certificate - * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. + * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. + * Copyright (C) 2001-2019 Werner Koch + * Copyright (C) 2015-2020 g10 Code GmbH * * This file is part of GnuPG. * @@ -15,6 +17,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: GPL-3.0-or-later */ #include <config.h> @@ -220,6 +223,53 @@ pk_algo_from_sexp (gcry_sexp_t pkey) } +/* Return the hash algorithm's algo id from its name given in the + * non-null termnated string in (buffer,buflen). Returns 0 on failure + * or if the algo is not known. */ +static int +hash_algo_from_buffer (const void *buffer, size_t buflen) +{ + char *string; + int algo; + + string = xtrymalloc (buflen + 1); + if (!string) + { + log_error (_("out of core\n")); + return 0; + } + memcpy (string, buffer, buflen); + string[buflen] = 0; + algo = gcry_md_map_name (string); + if (!algo) + log_error ("unknown digest algorithm '%s' used in certificate\n", string); + xfree (string); + return algo; +} + + +/* Return an unsigned integer from the non-null termnated string + * (buffer,buflen). Returns 0 on failure. */ +static unsigned int +uint_from_buffer (const void *buffer, size_t buflen) +{ + char *string; + unsigned int val; + + string = xtrymalloc (buflen + 1); + if (!string) + { + log_error (_("out of core\n")); + return 0; + } + memcpy (string, buffer, buflen); + string[buflen] = 0; + val = strtoul (string, NULL, 10); + xfree (string); + return val; +} + + /* Check the signature on CERT using the ISSUER-CERT. This function does only test the cryptographic signature and nothing else. It is assumed that the ISSUER_CERT is valid. */ @@ -229,66 +279,95 @@ gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) const char *algoid; gcry_md_hd_t md; int rc, algo; - gcry_mpi_t frame; ksba_sexp_t p; size_t n; - gcry_sexp_t s_sig, s_hash, s_pkey; + gcry_sexp_t s_sig, s_data, s_pkey; + int use_pss = 0; + unsigned int saltlen; algo = gcry_md_map_name ( (algoid=ksba_cert_get_digest_algo (cert))); - if (!algo) + if (!algo && algoid && !strcmp (algoid, "1.2.840.113549.1.1.10")) + use_pss = 1; + else if (!algo) { - log_error ("unknown hash algorithm '%s'\n", algoid? algoid:"?"); + log_error ("unknown digest algorithm '%s' used certificate\n", + algoid? algoid:"?"); if (algoid && ( !strcmp (algoid, "1.2.840.113549.1.1.2") ||!strcmp (algoid, "1.2.840.113549.2.2"))) log_info (_("(this is the MD2 algorithm)\n")); return gpg_error (GPG_ERR_GENERAL); } - rc = gcry_md_open (&md, algo, 0); - if (rc) - { - log_error ("md_open failed: %s\n", gpg_strerror (rc)); - return rc; - } - if (DBG_HASHING) - gcry_md_debug (md, "hash.cert"); - - rc = ksba_cert_hash (cert, 1, HASH_FNC, md); - if (rc) - { - log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (rc)); - gcry_md_close (md); - return rc; - } - gcry_md_final (md); + /* The the signature from the certificate. */ p = ksba_cert_get_sig_val (cert); n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) { log_error ("libksba did not return a proper S-Exp\n"); - gcry_md_close (md); ksba_free (p); return gpg_error (GPG_ERR_BUG); } + rc = gcry_sexp_sscan ( &s_sig, NULL, (char*)p, n); + ksba_free (p); + if (rc) + { + log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); + return rc; + } if (DBG_CRYPTO) + gcry_log_debugsxp ("sigval", s_sig); + + if (use_pss) { - int j; - log_debug ("signature value:"); - for (j=0; j < n; j++) - log_printf (" %02X", p[j]); - log_printf ("\n"); + /* Extract the hash algorithm and the salt length from the sigval. */ + gcry_buffer_t ioarray[2] = { {0}, {0} }; + + rc = gcry_sexp_extract_param (s_sig, "sig-val", + "&'hash-algo''salt-length'", + ioarray+0, ioarray+1, NULL); + if (rc) + { + gcry_sexp_release (s_sig); + log_error ("extracting params from PSS failed: %s\n", + gpg_strerror (rc)); + return rc; + } + algo = hash_algo_from_buffer (ioarray[0].data, ioarray[0].len); + saltlen = uint_from_buffer (ioarray[1].data, ioarray[1].len); + xfree (ioarray[0].data); + xfree (ioarray[1].data); + if (saltlen < 20) + { + log_error ("length of PSS salt too short\n"); + return gpg_error (GPG_ERR_DIGEST_ALGO); + } + if (!algo) + return gpg_error (GPG_ERR_DIGEST_ALGO); + /* log_debug ("PSS hash=%d saltlen=%u\n", algo, saltlen); */ } - rc = gcry_sexp_sscan ( &s_sig, NULL, (char*)p, n); - ksba_free (p); + + /* Hash the to-be-signed parts of the certificate. */ + rc = gcry_md_open (&md, algo, 0); if (rc) { - log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); + log_error ("md_open failed: %s\n", gpg_strerror (rc)); + return rc; + } + if (DBG_HASHING) + gcry_md_debug (md, "hash.cert"); + + rc = ksba_cert_hash (cert, 1, HASH_FNC, md); + if (rc) + { + log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (rc)); gcry_md_close (md); return rc; } + gcry_md_final (md); + /* Get the public key from the certificate. */ p = ksba_cert_get_public_key (issuer_cert); n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) @@ -308,29 +387,50 @@ gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) gcry_sexp_release (s_sig); return rc; } + if (DBG_CRYPTO) + gcry_log_debugsxp ("pubkey:", s_pkey); - rc = do_encode_md (md, algo, pk_algo_from_sexp (s_pkey), - gcry_pk_get_nbits (s_pkey), s_pkey, &frame); - if (rc) + if (use_pss) { - gcry_md_close (md); - gcry_sexp_release (s_sig); - gcry_sexp_release (s_pkey); - return rc; + rc = gcry_sexp_build (&s_data, NULL, + "(data (flags pss)" + "(hash %s %b)" + "(salt-length %u))", + hash_algo_to_string (algo), + (int)gcry_md_get_algo_dlen (algo), + gcry_md_read (md, algo), + saltlen); + if (rc) + BUG (); } + else + { + /* RSA or DAS: Prepare the hash for verification. */ + gcry_mpi_t frame; - /* put hash into the S-Exp s_hash */ - if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) ) - BUG (); - gcry_mpi_release (frame); - + rc = do_encode_md (md, algo, pk_algo_from_sexp (s_pkey), + gcry_pk_get_nbits (s_pkey), s_pkey, &frame); + if (rc) + { + gcry_md_close (md); + gcry_sexp_release (s_sig); + gcry_sexp_release (s_pkey); + return rc; + } + if ( gcry_sexp_build (&s_data, NULL, "%m", frame) ) + BUG (); + gcry_mpi_release (frame); + } + if (DBG_CRYPTO) + gcry_log_debugsxp ("data:", s_data); - rc = gcry_pk_verify (s_sig, s_hash, s_pkey); + /* Verify. */ + rc = gcry_pk_verify (s_sig, s_data, s_pkey); if (DBG_X509) log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc)); gcry_md_close (md); gcry_sexp_release (s_sig); - gcry_sexp_release (s_hash); + gcry_sexp_release (s_data); gcry_sexp_release (s_pkey); return rc; } |