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 <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-04-09 12:18:08 +02:00
parent 4d37cc72b8
commit 0626cc8fed
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
2 changed files with 308 additions and 61 deletions

View File

@ -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 /* 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 the CRL and the output is the hash context MD as well as the uses
algorithm identifier ALGO. */ algorithm identifier ALGO. */
static gpg_error_t 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; gpg_error_t err;
const char *algoid; const char *algoid;
*use_pss = 0;
algoid = ksba_crl_get_digest_algo (crl); 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) if (!*algo)
{ {
log_error (_("unknown hash algorithm '%s'\n"), algoid? algoid:"?"); 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. */ certificate of the CRL issuer. This function takes ownership of MD. */
static gpg_error_t static gpg_error_t
finish_sig_check (ksba_crl_t crl, gcry_md_hd_t md, int algo, 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; gpg_error_t err;
ksba_sexp_t sigval = NULL, pubkey = NULL; ksba_sexp_t sigval = NULL, pubkey = NULL;
const char *s;
char algoname[50];
size_t n; size_t n;
gcry_sexp_t s_sig = NULL, s_hash = NULL, s_pkey = NULL; 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. */ /* This also stops debugging on the MD. */
gcry_md_final (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; 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. */ /* Get and convert the public key for the issuer certificate. */
if (DBG_X509) if (DBG_X509)
dump_cert ("crl_issuer_cert", issuer_cert); 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. */ /* Create an S-expression with the actual hash value. */
s = gcry_md_algo_name (algo); if (use_pss)
for (i = 0; *s && i < sizeof(algoname) - 1; s++, i++) {
algoname[i] = ascii_tolower (*s); err = gcry_sexp_build (&s_hash, NULL,
algoname[i] = 0; "(data (flags pss)"
err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", "(hash %s %b)"
algoname, "(salt-length %u))",
gcry_md_get_algo_dlen (algo), gcry_md_read (md, algo)); 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) if (err)
{ {
log_error (_("creating S-expression failed: %s\n"), gcry_strerror (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; ksba_cert_t crlissuer_cert = NULL;
gcry_md_hd_t md = NULL; gcry_md_hd_t md = NULL;
int algo = 0; int algo = 0;
int use_pss = 0;
size_t n; size_t n;
(void)fname; (void)fname;
@ -1710,7 +1857,7 @@ crl_parse_insert (ctrl_t ctrl, ksba_crl_t crl,
{ {
case KSBA_SR_BEGIN_ITEMS: case KSBA_SR_BEGIN_ITEMS:
{ {
err = start_sig_check (crl, &md, &algo); err = start_sig_check (crl, &md, &algo, &use_pss);
if (err) if (err)
goto failure; goto failure;
@ -1847,7 +1994,7 @@ crl_parse_insert (ctrl_t ctrl, ksba_crl_t crl,
goto failure; 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. */ md = NULL; /* Closed. */
if (err) if (err)
{ {

View File

@ -1,5 +1,7 @@
/* certcheck.c - check one certificate /* 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. * This file is part of GnuPG.
* *
@ -15,6 +17,7 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>. * along with this program; if not, see <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/ */
#include <config.h> #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 /* Check the signature on CERT using the ISSUER-CERT. This function
does only test the cryptographic signature and nothing else. It is does only test the cryptographic signature and nothing else. It is
assumed that the ISSUER_CERT is valid. */ 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; const char *algoid;
gcry_md_hd_t md; gcry_md_hd_t md;
int rc, algo; int rc, algo;
gcry_mpi_t frame;
ksba_sexp_t p; ksba_sexp_t p;
size_t n; 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))); 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 if (algoid
&& ( !strcmp (algoid, "1.2.840.113549.1.1.2") && ( !strcmp (algoid, "1.2.840.113549.1.1.2")
||!strcmp (algoid, "1.2.840.113549.2.2"))) ||!strcmp (algoid, "1.2.840.113549.2.2")))
log_info (_("(this is the MD2 algorithm)\n")); log_info (_("(this is the MD2 algorithm)\n"));
return gpg_error (GPG_ERR_GENERAL); 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); rc = gcry_md_open (&md, algo, 0);
if (rc) if (rc)
{ {
@ -262,33 +367,7 @@ gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert)
} }
gcry_md_final (md); gcry_md_final (md);
p = ksba_cert_get_sig_val (cert); /* Get the public key from the certificate. */
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;
}
p = ksba_cert_get_public_key (issuer_cert); p = ksba_cert_get_public_key (issuer_cert);
n = gcry_sexp_canon_len (p, 0, NULL, NULL); n = gcry_sexp_canon_len (p, 0, NULL, NULL);
if (!n) if (!n)
@ -308,29 +387,50 @@ gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert)
gcry_sexp_release (s_sig); gcry_sexp_release (s_sig);
return rc; return rc;
} }
if (DBG_CRYPTO)
gcry_log_debugsxp ("pubkey:", s_pkey);
rc = do_encode_md (md, algo, pk_algo_from_sexp (s_pkey), if (use_pss)
gcry_pk_get_nbits (s_pkey), s_pkey, &frame);
if (rc)
{ {
gcry_md_close (md); rc = gcry_sexp_build (&s_data, NULL,
gcry_sexp_release (s_sig); "(data (flags pss)"
gcry_sexp_release (s_pkey); "(hash %s %b)"
return rc; "(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 */ rc = do_encode_md (md, algo, pk_algo_from_sexp (s_pkey),
if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) ) gcry_pk_get_nbits (s_pkey), s_pkey, &frame);
BUG (); if (rc)
gcry_mpi_release (frame); {
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);
/* Verify. */
rc = gcry_pk_verify (s_sig, s_hash, s_pkey); rc = gcry_pk_verify (s_sig, s_data, s_pkey);
if (DBG_X509) if (DBG_X509)
log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc)); log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc));
gcry_md_close (md); gcry_md_close (md);
gcry_sexp_release (s_sig); gcry_sexp_release (s_sig);
gcry_sexp_release (s_hash); gcry_sexp_release (s_data);
gcry_sexp_release (s_pkey); gcry_sexp_release (s_pkey);
return rc; return rc;
} }