diff --git a/dirmngr/crlcache.c b/dirmngr/crlcache.c index c9e5ca68f..73944667d 100644 --- a/dirmngr/crlcache.c +++ b/dirmngr/crlcache.c @@ -1531,17 +1531,104 @@ crl_cache_cert_isvalid (ctrl_t ctrl, ksba_cert_t cert, } +/* 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; +} + + /* Prepare a hash context for the signature verification. Input is the CRL and the output is the hash context MD as well as the uses algorithm identifier ALGO. */ static gpg_error_t -start_sig_check (ksba_crl_t crl, gcry_md_hd_t *md, int *algo) +start_sig_check (ksba_crl_t crl, gcry_md_hd_t *md, int *algo, int *use_pss) { gpg_error_t err; const char *algoid; + *use_pss = 0; algoid = ksba_crl_get_digest_algo (crl); - *algo = gcry_md_map_name (algoid); + if (algoid && !strcmp (algoid, "1.2.840.113549.1.1.10")) + { + /* Parse rsaPSS parameter. */ + gcry_buffer_t ioarray[1] = { {0} }; + ksba_sexp_t pssparam; + size_t n; + gcry_sexp_t psssexp; + + pssparam = ksba_crl_get_sig_val (crl); + n = gcry_sexp_canon_len (pssparam, 0, NULL, NULL); + if (!n) + { + ksba_free (pssparam); + log_error (_("got an invalid S-expression from libksba\n")); + return gpg_error (GPG_ERR_INV_SEXP); + } + err = gcry_sexp_sscan (&psssexp, NULL, pssparam, n); + ksba_free (pssparam); + if (err) + { + log_error (_("converting S-expression failed: %s\n"), + gcry_strerror (err)); + return err; + } + + err = gcry_sexp_extract_param (psssexp, "sig-val", + "&'hash-algo'", ioarray, NULL); + gcry_sexp_release (psssexp); + if (err) + { + log_error ("extracting params from PSS failed: %s\n", + gpg_strerror (err)); + return err; + } + *algo = hash_algo_from_buffer (ioarray[0].data, ioarray[0].len); + xfree (ioarray[0].data); + *use_pss = 1; + } + else + *algo = gcry_md_map_name (algoid); if (!*algo) { log_error (_("unknown hash algorithm '%s'\n"), algoid? algoid:"?"); @@ -1570,15 +1657,13 @@ start_sig_check (ksba_crl_t crl, gcry_md_hd_t *md, int *algo) certificate of the CRL issuer. This function takes ownership of MD. */ static gpg_error_t finish_sig_check (ksba_crl_t crl, gcry_md_hd_t md, int algo, - ksba_cert_t issuer_cert) + ksba_cert_t issuer_cert, int use_pss) { gpg_error_t err; ksba_sexp_t sigval = NULL, pubkey = NULL; - const char *s; - char algoname[50]; size_t n; gcry_sexp_t s_sig = NULL, s_hash = NULL, s_pkey = NULL; - unsigned int i; + unsigned int saltlen = 0; /* (used only with use_pss) */ /* This also stops debugging on the MD. */ gcry_md_final (md); @@ -1600,6 +1685,55 @@ finish_sig_check (ksba_crl_t crl, gcry_md_hd_t md, int algo, goto leave; } + if (use_pss) + { + /* Parse rsaPSS parameter which we should find in S_SIG. */ + gcry_buffer_t ioarray[2] = { {0}, {0} }; + ksba_sexp_t pssparam; + gcry_sexp_t psssexp; + int hashalgo; + + pssparam = ksba_crl_get_sig_val (crl); + n = gcry_sexp_canon_len (pssparam, 0, NULL, NULL); + if (!n) + { + ksba_free (pssparam); + log_error (_("got an invalid S-expression from libksba\n")); + err = gpg_error (GPG_ERR_INV_SEXP); + goto leave; + } + err = gcry_sexp_sscan (&psssexp, NULL, pssparam, n); + ksba_free (pssparam); + if (err) + { + log_error (_("converting S-expression failed: %s\n"), + gcry_strerror (err)); + goto leave; + } + + err = gcry_sexp_extract_param (psssexp, "sig-val", + "&'hash-algo''salt-length'", + ioarray+0, ioarray+1, NULL); + gcry_sexp_release (psssexp); + if (err) + { + log_error ("extracting params from PSS failed: %s\n", + gpg_strerror (err)); + goto leave; + } + hashalgo = 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 (hashalgo != algo) + { + log_error ("hash algo mismatch: %d announced but %d used\n", + algo, hashalgo); + return gpg_error (GPG_ERR_INV_CRL); + } + } + + /* Get and convert the public key for the issuer certificate. */ if (DBG_X509) dump_cert ("crl_issuer_cert", issuer_cert); @@ -1620,13 +1754,25 @@ finish_sig_check (ksba_crl_t crl, gcry_md_hd_t md, int algo, } /* Create an S-expression with the actual hash value. */ - s = gcry_md_algo_name (algo); - for (i = 0; *s && i < sizeof(algoname) - 1; s++, i++) - algoname[i] = ascii_tolower (*s); - algoname[i] = 0; - err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", - algoname, - gcry_md_get_algo_dlen (algo), gcry_md_read (md, algo)); + if (use_pss) + { + err = gcry_sexp_build (&s_hash, 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); + } + else + { + err = gcry_sexp_build (&s_hash, NULL, + "(data(flags pkcs1)(hash %s %b))", + hash_algo_to_string (algo), + (int)gcry_md_get_algo_dlen (algo), + gcry_md_read (md, algo)); + } if (err) { log_error (_("creating S-expression failed: %s\n"), gcry_strerror (err)); @@ -1688,6 +1834,7 @@ crl_parse_insert (ctrl_t ctrl, ksba_crl_t crl, ksba_cert_t crlissuer_cert = NULL; gcry_md_hd_t md = NULL; int algo = 0; + int use_pss = 0; size_t n; (void)fname; @@ -1710,7 +1857,7 @@ crl_parse_insert (ctrl_t ctrl, ksba_crl_t crl, { case KSBA_SR_BEGIN_ITEMS: { - err = start_sig_check (crl, &md, &algo); + err = start_sig_check (crl, &md, &algo, &use_pss); if (err) goto failure; @@ -1847,7 +1994,7 @@ crl_parse_insert (ctrl_t ctrl, ksba_crl_t crl, goto failure; } - err = finish_sig_check (crl, md, algo, crlissuer_cert); + err = finish_sig_check (crl, md, algo, crlissuer_cert, use_pss); md = NULL; /* Closed. */ if (err) { 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 . + * SPDX-License-Identifier: GPL-3.0-or-later */ #include @@ -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,21 +279,76 @@ 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); } + + /* 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"); + 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) + { + /* 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); */ + } + + + /* Hash the to-be-signed parts of the certificate. */ rc = gcry_md_open (&md, algo, 0); if (rc) { @@ -262,33 +367,7 @@ gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) } gcry_md_final (md); - 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); - } - if (DBG_CRYPTO) - { - int j; - log_debug ("signature value:"); - for (j=0; j < n; j++) - log_printf (" %02X", p[j]); - log_printf ("\n"); - } - - 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)); - gcry_md_close (md); - return rc; - } - + /* 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; }