diff options
Diffstat (limited to 'sm')
-rw-r--r-- | sm/certcheck.c | 73 | ||||
-rw-r--r-- | sm/decrypt.c | 4 | ||||
-rw-r--r-- | sm/encrypt.c | 5 | ||||
-rw-r--r-- | sm/fingerprint.c | 35 | ||||
-rw-r--r-- | sm/gpgsm.h | 10 | ||||
-rw-r--r-- | sm/keylist.c | 5 | ||||
-rw-r--r-- | sm/misc.c | 90 | ||||
-rw-r--r-- | sm/sign.c | 2 | ||||
-rw-r--r-- | sm/verify.c | 121 |
9 files changed, 237 insertions, 108 deletions
diff --git a/sm/certcheck.c b/sm/certcheck.c index 14f78dbe6..12b3ec927 100644 --- a/sm/certcheck.c +++ b/sm/certcheck.c @@ -492,69 +492,40 @@ gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) int -gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval, - gcry_md_hd_t md, int mdalgo, int *r_pkalgo) +gpgsm_check_cms_signature (ksba_cert_t cert, gcry_sexp_t s_sig, + gcry_md_hd_t md, int mdalgo, + unsigned int pkalgoflags, int *r_pkalgo) { int rc; ksba_sexp_t p; - gcry_sexp_t s_sig, s_hash, s_pkey, l1; + gcry_sexp_t s_hash, s_pkey; size_t n; - const char *s; - int i; int pkalgo; int use_pss; unsigned int saltlen = 0; - if (r_pkalgo) *r_pkalgo = 0; - n = gcry_sexp_canon_len (sigval, 0, NULL, NULL); - if (!n) - { - log_error ("libksba did not return a proper S-Exp\n"); - return gpg_error (GPG_ERR_BUG); - } - rc = gcry_sexp_sscan (&s_sig, NULL, (char*)sigval, n); - if (rc) + /* Check whether rsaPSS is needed. This information is indicated in + * the SIG-VAL and already provided to us by the caller so that we + * do not need to parse this out. */ + use_pss = !!(pkalgoflags & PK_ALGO_FLAG_RSAPSS); + if (use_pss) { - log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); - return rc; - } + int algo; - /* Check whether rsaPSS is needed. This is indicated in the SIG-VAL - * using a flag. Only if we found that flag, we extract the PSS - * parameters for SIG-VAL. */ - use_pss = 0; - l1 = gcry_sexp_find_token (s_sig, "flags", 0); - if (l1) - { - /* Note that the flag parser assumes that the list of flags - * contains only strings and in particular not sublist. This is - * always the case or current libksba. */ - for (i=1; (s = gcry_sexp_nth_data (l1, i, &n)); i++) - if (n == 3 && !memcmp (s, "pss", 3)) - { - use_pss = 1; - break; - } - gcry_sexp_release (l1); - if (use_pss) + rc = extract_pss_params (s_sig, &algo, &saltlen); + if (rc) { - int algo; - - rc = extract_pss_params (s_sig, &algo, &saltlen); - if (rc) - { - gcry_sexp_release (s_sig); - return rc; - } - if (algo != mdalgo) - { - log_error ("PSS hash algo mismatch (%d/%d)\n", mdalgo, algo); - gcry_sexp_release (s_sig); - return gpg_error (GPG_ERR_DIGEST_ALGO); - } + gcry_sexp_release (s_sig); + return rc; + } + if (algo != mdalgo) + { + log_error ("PSS hash algo mismatch (%d/%d)\n", mdalgo, algo); + gcry_sexp_release (s_sig); + return gpg_error (GPG_ERR_DIGEST_ALGO); } } @@ -564,7 +535,6 @@ gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval, { log_error ("libksba did not return a proper S-Exp\n"); ksba_free (p); - gcry_sexp_release (s_sig); return gpg_error (GPG_ERR_BUG); } if (DBG_CRYPTO) @@ -575,7 +545,6 @@ gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval, if (rc) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); - gcry_sexp_release (s_sig); return rc; } @@ -605,7 +574,6 @@ gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval, gcry_pk_get_nbits (s_pkey), s_pkey, &frame); if (rc) { - gcry_sexp_release (s_sig); gcry_sexp_release (s_pkey); return rc; } @@ -618,7 +586,6 @@ gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval, rc = gcry_pk_verify (s_sig, s_hash, s_pkey); if (DBG_X509) log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc)); - gcry_sexp_release (s_sig); gcry_sexp_release (s_hash); gcry_sexp_release (s_pkey); return rc; diff --git a/sm/decrypt.c b/sm/decrypt.c index 904a40235..90eba888d 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -483,7 +483,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) /* Check compliance. */ if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION, - pk_algo, NULL, nbits, NULL)) + pk_algo, 0, NULL, nbits, NULL)) { char kidstr[10+1]; @@ -501,7 +501,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) /* Check that all certs are compliant with CO_DE_VS. */ is_de_vs = (is_de_vs - && gnupg_pk_is_compliant (CO_DE_VS, pk_algo, NULL, + && gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0, NULL, nbits, NULL)); } diff --git a/sm/encrypt.c b/sm/encrypt.c index 6213a6604..f03097c84 100644 --- a/sm/encrypt.c +++ b/sm/encrypt.c @@ -480,7 +480,8 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) /* Check compliance. */ pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits); - if (!gnupg_pk_is_compliant (opt.compliance, pk_algo, NULL, nbits, NULL)) + if (!gnupg_pk_is_compliant (opt.compliance, pk_algo, 0, + NULL, nbits, NULL)) { char kidstr[10+1]; @@ -495,7 +496,7 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) /* Fixme: When adding ECC we need to provide the curvename and * the key to gnupg_pk_is_compliant. */ if (compliant - && !gnupg_pk_is_compliant (CO_DE_VS, pk_algo, NULL, nbits, NULL)) + && !gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0, NULL, nbits, NULL)) compliant = 0; rc = encrypt_dek (dek, cl->cert, &encval); diff --git a/sm/fingerprint.c b/sm/fingerprint.c index 3a57b7ba5..2e01cf1c0 100644 --- a/sm/fingerprint.c +++ b/sm/fingerprint.c @@ -277,6 +277,41 @@ gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits) } +/* This is a wrapper around pubkey_algo_string which takes a KSBA + * certificate instead of a Gcrypt public key. Note that this + * function may return NULL on error. */ +char * +gpgsm_pubkey_algo_string (ksba_cert_t cert, int *r_algoid) +{ + gpg_error_t err; + gcry_sexp_t s_pkey; + ksba_sexp_t p; + size_t n; + enum gcry_pk_algos algoid; + char *algostr; + + p = ksba_cert_get_public_key (cert); + if (!p) + return NULL; + n = gcry_sexp_canon_len (p, 0, NULL, NULL); + if (!n) + { + xfree (p); + return NULL; + } + err = gcry_sexp_sscan (&s_pkey, NULL, (char *)p, n); + xfree (p); + if (err) + return NULL; + + algostr = pubkey_algo_string (s_pkey, r_algoid? &algoid : NULL); + if (algostr && r_algoid) + *r_algoid = algoid; + + gcry_sexp_release (s_pkey); + return algostr; +} + /* For certain purposes we need a certificate id which has an upper diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 268c2d054..d1283440d 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -264,6 +264,7 @@ unsigned long gpgsm_get_short_fingerprint (ksba_cert_t cert, unsigned char *gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array); char *gpgsm_get_keygrip_hexstring (ksba_cert_t cert); int gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits); +char *gpgsm_pubkey_algo_string (ksba_cert_t cert, int *r_algoid); char *gpgsm_get_certid (ksba_cert_t cert); @@ -295,8 +296,10 @@ char *gpgsm_format_keydesc (ksba_cert_t cert); /*-- certcheck.c --*/ int gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert); -int gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval, - gcry_md_hd_t md, int hash_algo, int *r_pkalgo); +int gpgsm_check_cms_signature (ksba_cert_t cert, gcry_sexp_t sigval, + gcry_md_hd_t md, + int hash_algo, unsigned int pkalgoflags, + int *r_pkalgo); /* fixme: move create functions to another file */ int gpgsm_create_cms_signature (ctrl_t ctrl, ksba_cert_t cert, gcry_md_hd_t md, int mdalgo, @@ -438,6 +441,9 @@ gpg_error_t transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo, unsigned char **r_newsigval, size_t *r_newsigvallen); +gcry_sexp_t gpgsm_ksba_cms_get_sig_val (ksba_cms_t cms, int idx); +int gpgsm_get_hash_algo_from_sigval (gcry_sexp_t sigval, + unsigned int *r_pkalgo_flags); diff --git a/sm/keylist.c b/sm/keylist.c index 7961b66fd..8c7fafc28 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -361,7 +361,10 @@ print_compliance_flags (ksba_cert_t cert, int algo, unsigned int nbits, { int hashalgo; - if (gnupg_pk_is_compliant (CO_DE_VS, algo, NULL, nbits, NULL)) + /* Note that we do not need to test for PK_ALGO_FLAG_RSAPSS because + * that is not a property of the key but one of the created + * signature. */ + if (gnupg_pk_is_compliant (CO_DE_VS, algo, 0, NULL, nbits, NULL)) { hashalgo = gcry_md_map_name (ksba_cert_get_digest_algo (cert)); if (gnupg_digest_is_compliant (CO_DE_VS, hashalgo)) @@ -216,3 +216,93 @@ transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo, return err; } + + +/* Wrapper around ksba_cms_get_sig_val to return a gcrypt object + * instaed of ksba's canonical s-expression. On errror NULL is return + * and in some cases an error message is printed. */ +gcry_sexp_t +gpgsm_ksba_cms_get_sig_val (ksba_cms_t cms, int idx) +{ + gpg_error_t err; + ksba_sexp_t sigval; + gcry_sexp_t s_sigval; + size_t n; + + sigval = ksba_cms_get_sig_val (cms, idx); + if (!sigval) + return NULL; + n = gcry_sexp_canon_len (sigval, 0, NULL, NULL); + if (!n) + { + log_error ("%s: libksba did not return a proper S-Exp\n", __func__); + ksba_free (sigval); + return NULL; + } + err = gcry_sexp_sscan (&s_sigval, NULL, (char*)sigval, n); + ksba_free (sigval); + if (err) + { + log_error ("%s: gcry_sexp_scan failed: %s\n", + __func__, gpg_strerror (err)); + s_sigval = NULL; + } + + return s_sigval; +} + + +/* Return the hash algorithm from the S-expression SIGVAL. Returns 0 + * if the hash algorithm is not encoded in SIGVAL or it is not + * supported by libgcrypt. It further stores flag values for the + * public key algorithm at R_PKALGO_FLAGS; the only flag we currently + * support is PK_ALGO_FLAG_RSAPSS. */ +int +gpgsm_get_hash_algo_from_sigval (gcry_sexp_t sigval_arg, + unsigned int *r_pkalgo_flags) +{ + gcry_sexp_t sigval, l1; + size_t n; + const char *s; + char *string; + int hashalgo; + int i; + + *r_pkalgo_flags = 0; + + sigval = gcry_sexp_find_token (sigval_arg, "sig-val", 0); + if (!sigval) + return 0; /* Not a sig-val. */ + + /* First check whether this is a rsaPSS signature and return that as + * additional info. */ + l1 = gcry_sexp_find_token (sigval, "flags", 0); + if (l1) + { + /* Note that the flag parser assumes that the list of flags + * contains only strings and in particular not a sub-list. This + * is always the case for the current libksba. */ + for (i=1; (s = gcry_sexp_nth_data (l1, i, &n)); i++) + if (n == 3 && !memcmp (s, "pss", 3)) + { + *r_pkalgo_flags |= PK_ALGO_FLAG_RSAPSS; + break; + } + gcry_sexp_release (l1); + } + + l1 = gcry_sexp_find_token (sigval, "hash", 0); + if (!l1) + { + gcry_sexp_release (sigval); + return 0; /* hash algorithm not given in sigval. */ + } + string = gcry_sexp_nth_string (l1, 1); + gcry_sexp_release (sigval); + if (!string) + return 0; /* hash algorithm has no value. */ + hashalgo = gcry_md_map_name (string); + gcry_free (string); + + return hashalgo; +} @@ -486,7 +486,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, unsigned int nbits; int pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits); - if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pk_algo, + if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pk_algo, 0, NULL, nbits, NULL)) { char kidstr[10+1]; diff --git a/sm/verify.c b/sm/verify.c index 6d2f11055..0fa365fb7 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -1,6 +1,8 @@ /* verify.c - Verify a messages signature * Copyright (C) 2001, 2002, 2003, 2007, * 2010 Free Software Foundation, Inc. + * Copyright (C) 2001-2019 Werner Koch + * Copyright (C) 2015-2020 g10 Code GmbH * * This file is part of GnuPG. * @@ -16,6 +18,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> @@ -286,7 +289,7 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) for (signer=0; ; signer++) { char *issuer = NULL; - ksba_sexp_t sigval = NULL; + gcry_sexp_t sigval = NULL; ksba_isotime_t sigtime, keyexptime; ksba_sexp_t serial; char *msgdigest = NULL; @@ -294,7 +297,11 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) char *ctattr; int sigval_hash_algo; int info_pkalgo; - unsigned int verifyflags; + unsigned int nbits; + int pkalgo; + char *pkalgostr = NULL; + char *pkfpr = NULL; + unsigned int pkalgoflags, verifyflags; rc = ksba_cms_get_issuer_serial (cms, signer, &issuer, &serial); if (!signer && gpg_err_code (rc) == GPG_ERR_NO_DATA @@ -400,20 +407,19 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) rc = 0; - sigval = ksba_cms_get_sig_val (cms, signer); + sigval = gpgsm_ksba_cms_get_sig_val (cms, signer); if (!sigval) { log_error ("no signature value available\n"); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad"); goto next_signer; } - sigval_hash_algo = hash_algo_from_sigval (sigval); + + sigval_hash_algo = gpgsm_get_hash_algo_from_sigval (sigval, &pkalgoflags); if (DBG_X509) { - log_debug ("signer %d - signature available (sigval hash=%d)", - signer, sigval_hash_algo); - /*log_printhex(sigval, gcry_sexp_canon_len (sigval, 0, NULL, NULL),*/ - /* "sigval "); */ + log_debug ("signer %d - signature available (sigval hash=%d pkaf=%u)", + signer, sigval_hash_algo, pkalgoflags); } if (!sigval_hash_algo) sigval_hash_algo = algo; /* Fallback used e.g. with old libksba. */ @@ -450,49 +456,68 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) goto next_signer; } - /* Check compliance. */ - { - unsigned int nbits; - int pk_algo = gpgsm_get_key_algo_info (cert, &nbits); - - if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION, - pk_algo, NULL, nbits, NULL)) - { - char kidstr[10+1]; - - snprintf (kidstr, sizeof kidstr, "0x%08lX", - gpgsm_get_short_fingerprint (cert, NULL)); - log_error (_("key %s may not be used for signing in %s mode\n"), - kidstr, - gnupg_compliance_option_string (opt.compliance)); - goto next_signer; - } - - if (! gnupg_digest_is_allowed (opt.compliance, 0, sigval_hash_algo)) - { - log_error (_("digest algorithm '%s' may not be used in %s mode\n"), - gcry_md_algo_name (sigval_hash_algo), - gnupg_compliance_option_string (opt.compliance)); - goto next_signer; - } - - /* Check compliance with CO_DE_VS. */ - if (gnupg_pk_is_compliant (CO_DE_VS, pk_algo, NULL, nbits, NULL) - && gnupg_digest_is_compliant (CO_DE_VS, sigval_hash_algo)) - gpgsm_status (ctrl, STATUS_VERIFICATION_COMPLIANCE_MODE, - gnupg_status_compliance_flag (CO_DE_VS)); - } + pkfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); + pkalgostr = gpgsm_pubkey_algo_string (cert, NULL); + pkalgo = gpgsm_get_key_algo_info (cert, &nbits); log_info (_("Signature made ")); if (*sigtime) - dump_isotime (sigtime); + { + /* We take the freedom as noted in RFC3339 to use a space + * instead of the "T" delimiter between date and time. We + * also append a separate UTC instead of a "Z" or "+00:00" + * suffix because that makes it clear to everyone what kind + * of time this is. */ + dump_isotime (sigtime); + log_printf (" UTC"); + } else log_printf (_("[date not given]")); - log_printf (_(" using certificate ID 0x%08lX\n"), - gpgsm_get_short_fingerprint (cert, NULL)); + log_info (_(" using %s key %s\n"), pkalgostr, pkfpr); + if (opt.verbose) + { + log_info (_("algorithm:")); + log_printf (" %s + %s", + pubkey_algo_to_string (pkalgo), + gcry_md_algo_name (sigval_hash_algo)); + if (algo != sigval_hash_algo) + log_printf (" (%s)", gcry_md_algo_name (algo)); + log_printf ("\n"); + } audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo); + /* Check compliance. */ + if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION, + pkalgo, pkalgoflags, NULL, nbits, NULL)) + { + char kidstr[10+1]; + + snprintf (kidstr, sizeof kidstr, "0x%08lX", + gpgsm_get_short_fingerprint (cert, NULL)); + log_error (_("key %s may not be used for signing in %s mode\n"), + kidstr, + gnupg_compliance_option_string (opt.compliance)); + goto next_signer; + } + + if (!gnupg_digest_is_allowed (opt.compliance, 0, sigval_hash_algo)) + { + log_error (_("digest algorithm '%s' may not be used in %s mode\n"), + gcry_md_algo_name (sigval_hash_algo), + gnupg_compliance_option_string (opt.compliance)); + goto next_signer; + } + + /* Check compliance with CO_DE_VS. */ + if (gnupg_pk_is_compliant (CO_DE_VS, pkalgo, pkalgoflags, + NULL, nbits, NULL) + && gnupg_digest_is_compliant (CO_DE_VS, sigval_hash_algo)) + gpgsm_status (ctrl, STATUS_VERIFICATION_COMPLIANCE_MODE, + gnupg_status_compliance_flag (CO_DE_VS)); + + + /* Now we can check the signature. */ if (msgdigest) { /* Signed attributes are available. */ gcry_md_hd_t md; @@ -545,14 +570,14 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error"); goto next_signer; } - rc = gpgsm_check_cms_signature (cert, sigval, md, - sigval_hash_algo, &info_pkalgo); + rc = gpgsm_check_cms_signature (cert, sigval, md, sigval_hash_algo, + pkalgoflags, &info_pkalgo); gcry_md_close (md); } else { rc = gpgsm_check_cms_signature (cert, sigval, data_md, - algo, &info_pkalgo); + algo, pkalgoflags, &info_pkalgo); } if (rc) @@ -669,8 +694,10 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) rc = 0; xfree (issuer); xfree (serial); - xfree (sigval); + gcry_sexp_release (sigval); xfree (msgdigest); + xfree (pkalgostr); + xfree (pkfpr); ksba_cert_release (cert); cert = NULL; } |