aboutsummaryrefslogtreecommitdiffstats
path: root/sm/certcheck.c
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2020-04-09 10:18:08 +0000
committerWerner Koch <[email protected]>2020-04-09 10:20:19 +0000
commitb45ab0ca08f8d6f9831192210b9ab141f4e450cf (patch)
tree114b0ef5f8e004c4a7c679decfd68af4ebf5708f /sm/certcheck.c
parentcommon: New function to map hash algo names. (diff)
downloadgnupg-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.c190
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;
}