1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

sm: Exclude rsaPSS from de-vs compliance mode.

* common/compliance.h (PK_ALGO_FLAG_RSAPSS): New.
* common/compliance.c (gnupg_pk_is_compliant): Add arg alog_flags and
test rsaPSS.  Adjust all callers.
(gnupg_pk_is_allowed): Ditto.
* sm/misc.c (gpgsm_ksba_cms_get_sig_val): New wrapper function.
(gpgsm_get_hash_algo_from_sigval): New.
* sm/certcheck.c (gpgsm_check_cms_signature): Change type of sigval
arg.  Add arg pkalgoflags.  Use the PK_ALGO_FLAG_RSAPSS.
* sm/verify.c (gpgsm_verify): Use the new wrapper and new fucntion to
also get the algo flags.  Pass algo flags along.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-07-03 15:47:55 +02:00
parent c1663c690b
commit 969abcf40c
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
16 changed files with 175 additions and 89 deletions

View File

@ -96,6 +96,7 @@ gnupg_initialize_compliance (int gnupg_module_name)
* both are compatible from the point of view of this function. */
int
gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo,
unsigned int algo_flags,
gcry_mpi_t key[], unsigned int keylength,
const char *curvename)
{
@ -148,6 +149,10 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo,
result = (keylength == 2048
|| keylength == 3072
|| keylength == 4096);
/* rsaPSS was not part of the evaluation and thus we don't
* claim compliance. */
if ((algo_flags & PK_ALGO_FLAG_RSAPSS))
result = 0;
break;
case is_dsa:
@ -197,7 +202,8 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo,
* they produce, and liberal in what they accept. */
int
gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance,
enum pk_use_case use, int algo, gcry_mpi_t key[],
enum pk_use_case use, int algo,
unsigned int algo_flags, gcry_mpi_t key[],
unsigned int keylength, const char *curvename)
{
int result = 0;
@ -228,6 +234,10 @@ gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance,
default:
log_assert (!"reached");
}
/* rsaPSS was not part of the evaluation and thus we don't
* claim compliance. */
if ((algo_flags & PK_ALGO_FLAG_RSAPSS))
result = 0;
break;
case PUBKEY_ALGO_DSA:

View File

@ -48,11 +48,17 @@ enum pk_use_case
PK_USE_SIGNING, PK_USE_VERIFICATION,
};
/* Flags to distinguish public key algorithm variants. */
#define PK_ALGO_FLAG_RSAPSS 1 /* Use rsaPSS padding. */
int gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo,
unsigned int algo_flags,
gcry_mpi_t key[], unsigned int keylength,
const char *curvename);
int gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance,
enum pk_use_case use, int algo, gcry_mpi_t key[],
enum pk_use_case use, int algo,
unsigned int algo_flags, gcry_mpi_t key[],
unsigned int keylength, const char *curvename);
int gnupg_cipher_is_compliant (enum gnupg_compliance_mode compliance,
cipher_algo_t cipher,

View File

@ -785,15 +785,15 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
PKT_public_key *pk = pkr->pk;
unsigned int nbits = nbits_from_pk (pk);
if (!gnupg_pk_is_compliant (opt.compliance,
pk->pubkey_algo, pk->pkey, nbits, NULL))
if (!gnupg_pk_is_compliant (opt.compliance, pk->pubkey_algo, 0,
pk->pkey, nbits, NULL))
log_info (_("WARNING: key %s is not suitable for encryption"
" in %s mode\n"),
keystr_from_pk (pk),
gnupg_compliance_option_string (opt.compliance));
if (compliant
&& !gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, pk->pkey,
&& !gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0, pk->pkey,
nbits, NULL))
compliant = 0;
}

View File

@ -1390,7 +1390,7 @@ print_compliance_flags (PKT_public_key *pk,
es_fputs (gnupg_status_compliance_flag (CO_GNUPG), es_stdout);
any++;
}
if (gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, pk->pkey,
if (gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0, pk->pkey,
keylength, curvename))
{
es_fprintf (es_stdout, any ? " %s" : "%s",

View File

@ -702,8 +702,8 @@ proc_encrypted (CTX c, PACKET *pkt)
memset (pk, 0, sizeof *pk);
pk->pubkey_algo = i->pubkey_algo;
if (get_pubkey (c->ctrl, pk, i->keyid) != 0
|| ! gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, pk->pkey,
nbits_from_pk (pk), NULL))
|| ! gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0,
pk->pkey, nbits_from_pk (pk), NULL))
compliant = 0;
release_public_key_parts (pk);
}
@ -2449,7 +2449,7 @@ check_sig_and_print (CTX c, kbnode_t node)
/* Print compliance warning for Good signatures. */
if (!rc && pk && !opt.quiet
&& !gnupg_pk_is_compliant (opt.compliance, pk->pubkey_algo,
&& !gnupg_pk_is_compliant (opt.compliance, pk->pubkey_algo, 0,
pk->pkey, nbits_from_pk (pk), NULL))
{
log_info (_("WARNING: This key is not suitable for signing"
@ -2534,7 +2534,7 @@ check_sig_and_print (CTX c, kbnode_t node)
/* Compute compliance with CO_DE_VS. */
if (pk && is_status_enabled ()
&& gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, pk->pkey,
&& gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0, pk->pkey,
nbits_from_pk (pk), NULL)
&& gnupg_digest_is_compliant (CO_DE_VS, sig->digest_algo))
write_status_strings (STATUS_VERIFICATION_COMPLIANCE_MODE,

View File

@ -96,7 +96,7 @@ get_session_key (ctrl_t ctrl, struct pubkey_enc_list *list, DEK *dek)
/* Check compliance. */
if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION,
sk->pubkey_algo,
sk->pubkey_algo, 0,
sk->pkey, nbits_from_pk (sk), NULL))
{
log_info (_("key %s is not suitable for decryption"

View File

@ -173,7 +173,7 @@ check_signature2 (ctrl_t ctrl,
else if (get_pubkey_for_sig (ctrl, pk, sig, forced_pk))
rc = gpg_error (GPG_ERR_NO_PUBKEY);
else if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION,
pk->pubkey_algo, pk->pkey,
pk->pubkey_algo, 0, pk->pkey,
nbits_from_pk (pk),
NULL))
{

View File

@ -456,7 +456,8 @@ do_sign (ctrl_t ctrl, PKT_public_key *pksk, PKT_signature *sig,
goto leave;
}
if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pksk->pubkey_algo,
if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING,
pksk->pubkey_algo, 0,
pksk->pkey, nbits_from_pk (pksk), NULL))
{
log_error (_("key %s may not be used for signing in %s mode\n"),

View File

@ -606,69 +606,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)
/* 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 ("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)
{
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);
}
}
@ -678,7 +649,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)
@ -689,7 +659,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;
}
@ -719,7 +688,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;
}
@ -732,7 +700,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;

View File

@ -871,7 +871,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];
@ -887,7 +887,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,
&& gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0,
NULL, nbits, NULL));
oops:

View File

@ -757,7 +757,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];
@ -772,7 +773,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, pk_algo, &encval);

View File

@ -325,8 +325,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,
@ -472,6 +474,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);

View File

@ -395,7 +395,10 @@ print_compliance_flags (ksba_cert_t cert, int algo, unsigned int nbits,
int indent = 0;
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))

View File

@ -284,3 +284,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;
}

View File

@ -601,7 +601,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
goto leave;
}
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];

View File

@ -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;
@ -298,7 +301,7 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp)
int pkalgo;
char *pkalgostr = NULL;
char *pkfpr = NULL;
unsigned int verifyflags;
unsigned int pkalgoflags, verifyflags;
rc = ksba_cms_get_issuer_serial (cms, signer, &issuer, &serial);
if (!signer && gpg_err_code (rc) == GPG_ERR_NO_DATA
@ -404,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. */
@ -492,7 +494,7 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp)
/* Check compliance. */
if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION,
pkalgo, NULL, nbits, NULL))
pkalgo, pkalgoflags, NULL, nbits, NULL))
{
char kidstr[10+1];
@ -513,7 +515,8 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp)
}
/* Check compliance with CO_DE_VS. */
if (gnupg_pk_is_compliant (CO_DE_VS, pkalgo, NULL, nbits, NULL)
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));
@ -572,14 +575,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)
@ -623,7 +626,7 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp)
xfree (fpr);
/* FIXME: INFO_PKALGO correctly shows ECDSA but PKALGO is then
* ECC. We should use the ECDS here and need to find a way to
* ECC. We should use the ECDSA here and need to find a way to
* figure this oult without using the bodus assumtion in
* gpgsm_check_cms_signature that ECC is alwas ECDSA. */
@ -702,7 +705,7 @@ 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);