From b85c0706db871828f0bc4672571dd0b9c98dd835 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Sun, 22 Jul 2018 16:23:54 +0200
Subject: [PATCH 1/5] doc: Fix the pkcs11-tool example

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
---
 doc/tools/pkcs11-tool.1.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/doc/tools/pkcs11-tool.1.xml b/doc/tools/pkcs11-tool.1.xml
index 37093f352..c609ec0e2 100644
--- a/doc/tools/pkcs11-tool.1.xml
+++ b/doc/tools/pkcs11-tool.1.xml
@@ -568,7 +568,7 @@
 
 			To read the certificate with ID <replaceable>KEY_ID</replaceable>
 			in DER format from smart card:
-				<programlisting>pkcs11-tool --read-object  --id KEY_ID --type cert --outfile cert.der</programlisting>
+				<programlisting>pkcs11-tool --read-object  --id KEY_ID --type cert --output-file cert.der</programlisting>
 
 			To convert the certificate in DER format to PEM format, use OpenSSL
 			tools:

From 5cc144111acb7b9982ddec7f7597a22c10c4d456 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Fri, 14 Sep 2018 14:11:18 +0200
Subject: [PATCH 2/5] p11test: Add missing CKM_SHA224_RSA_PKCS_PSS

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
---
 src/tests/p11test/p11test_case_common.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/tests/p11test/p11test_case_common.c b/src/tests/p11test/p11test_case_common.c
index deb2a56fe..d44b0d8e3 100644
--- a/src/tests/p11test/p11test_case_common.c
+++ b/src/tests/p11test/p11test_case_common.c
@@ -587,6 +587,8 @@ const char *get_mechanism_name(int mech_id)
 			return "RSA_PKCS_PSS";
 		case CKM_SHA1_RSA_PKCS_PSS:
 			return "SHA1_RSA_PKCS_PSS";
+		case CKM_SHA224_RSA_PKCS_PSS:
+			return "SHA224_RSA_PKCS_PSS";
 		case CKM_SHA256_RSA_PKCS_PSS:
 			return "SHA256_RSA_PKCS_PSS";
 		case CKM_SHA384_RSA_PKCS_PSS:

From 5aa3dbcdd76af0197946252ff53a0636cb979ab3 Mon Sep 17 00:00:00 2001
From: Nicholas Wilson <nicholas.wilson@realvnc.com>
Date: Tue, 25 Aug 2015 12:45:27 +0100
Subject: [PATCH 3/5] Add support for PSS padding to RSA signatures

A card driver may declare support for computing the padding on the card,
or else the padding will be applied locally in padding.c.  All five
PKCS11 PSS mechanisms are supported, for signature and verification.

There are a few limits on what we choose to support, in particular I
don't see a need for arbitrary combinations of MGF hash, data hash, and
salt length, so I've restricted it (for the user's benefit) to the only
cases that really matter, where salt_len = hash_len and the same hash is
used for the MGF and data hashing.

------------------------------------------------------------------------
Reworked and extended in 2018 by Jakub Jelen <jjelen@redhat.com> against
current OpenSC master, to actually work with existing PIV cards:
 * extended of missing mechanisms (SHA224, possibility to select MGF1)
 * compatibility with OpenSSL 1.1+
 * Removed the ANSI padding
 * Formatting cleanup, error checking

Based on the original work from

https://github.com/NWilson/OpenSC/commit/42f3199e66

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
---
 src/libopensc/card-atrust-acos.c |   2 +-
 src/libopensc/card-starcos.c     |   4 +-
 src/libopensc/internal.h         |   2 +-
 src/libopensc/opensc.h           |  74 +++++++--
 src/libopensc/padding.c          | 257 ++++++++++++++++++++++++++----
 src/libopensc/pkcs15-sec.c       |  33 ++--
 src/pkcs11/framework-pkcs15.c    | 265 +++++++++++++++++++++++--------
 src/pkcs11/mechanism.c           |  31 +++-
 src/pkcs11/openssl.c             | 151 ++++++++++++++++--
 src/pkcs11/pkcs11.h              |   3 +-
 src/pkcs11/sc-pkcs11.h           |   9 +-
 11 files changed, 674 insertions(+), 157 deletions(-)

diff --git a/src/libopensc/card-atrust-acos.c b/src/libopensc/card-atrust-acos.c
index fb0b296c8..05ef0f441 100644
--- a/src/libopensc/card-atrust-acos.c
+++ b/src/libopensc/card-atrust-acos.c
@@ -722,7 +722,7 @@ static int atrust_acos_compute_signature(struct sc_card *card,
 				flags = SC_ALGORITHM_RSA_HASH_NONE;
 			tmp_len = sizeof(sbuf);
 			r = sc_pkcs1_encode(card->ctx, flags, data, datalen,
-					sbuf, &tmp_len, sizeof(sbuf));
+					sbuf, &tmp_len, sizeof(sbuf)*8);
 			if (r < 0)
 				return r;
 		} else {
diff --git a/src/libopensc/card-starcos.c b/src/libopensc/card-starcos.c
index 7ad132dc1..799c6a680 100644
--- a/src/libopensc/card-starcos.c
+++ b/src/libopensc/card-starcos.c
@@ -1545,7 +1545,7 @@ static int starcos_compute_signature(sc_card_t *card,
 					flags = SC_ALGORITHM_RSA_HASH_NONE;
 				}
 				tmp_len = sizeof(sbuf);
-				r = sc_pkcs1_encode(card->ctx, flags, data, datalen, sbuf, &tmp_len, sizeof(sbuf));
+				r = sc_pkcs1_encode(card->ctx, flags, data, datalen, sbuf, &tmp_len, sizeof(sbuf)*8);
 				SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_pkcs1_encode failed");
 			} else {
 				memcpy(sbuf, data, datalen);
@@ -1607,7 +1607,7 @@ static int starcos_compute_signature(sc_card_t *card,
 				flags = SC_ALGORITHM_RSA_HASH_NONE;
 			tmp_len = sizeof(sbuf);
 			r = sc_pkcs1_encode(card->ctx, flags, data, datalen,
-					sbuf, &tmp_len, sizeof(sbuf));
+					sbuf, &tmp_len, sizeof(sbuf)*8);
 			if (r < 0)
 				return r;
 		} else {
diff --git a/src/libopensc/internal.h b/src/libopensc/internal.h
index 9d6a77ffe..08d590f23 100644
--- a/src/libopensc/internal.h
+++ b/src/libopensc/internal.h
@@ -159,7 +159,7 @@ int sc_pkcs1_strip_digest_info_prefix(unsigned int *algorithm,
  * @return SC_SUCCESS on success and an error code otherwise
  */
 int sc_pkcs1_encode(sc_context_t *ctx, unsigned long flags,
-		const u8 *in, size_t inlen, u8 *out, size_t *outlen, size_t modlen);
+		const u8 *in, size_t inlen, u8 *out, size_t *outlen, size_t mod_bits);
 /**
  * Get the necessary padding and sec. env. flags.
  * @param  ctx     IN  sc_contex_t object
diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h
index b9b960d8f..a4e87d5bf 100644
--- a/src/libopensc/opensc.h
+++ b/src/libopensc/opensc.h
@@ -93,19 +93,39 @@ extern "C" {
 #define SC_ALGORITHM_NEED_USAGE		0x40000000
 #define SC_ALGORITHM_SPECIFIC_FLAGS	0x001FFFFF
 
-#define SC_ALGORITHM_RSA_RAW		0x00000001
 /* If the card is willing to produce a cryptogram padded with the following
- * methods, set these flags accordingly. */
-#define SC_ALGORITHM_RSA_PADS		0x0000001E
-#define SC_ALGORITHM_RSA_PAD_NONE	0x00000000
-#define SC_ALGORITHM_RSA_PAD_PKCS1	0x00000002
+ * methods, set these flags accordingly.  These flags are exclusive: an RSA card
+ * must support at least one of them, and exactly one of them must be selected
+ * for a given operation. */
+#define SC_ALGORITHM_RSA_RAW		0x00000001
+#define SC_ALGORITHM_RSA_PADS		0x0000001F
+#define SC_ALGORITHM_RSA_PAD_NONE	0x00000001
+#define SC_ALGORITHM_RSA_PAD_PKCS1	0x00000002 /* PKCS#1 v1.5 padding */
 #define SC_ALGORITHM_RSA_PAD_ANSI	0x00000004
 #define SC_ALGORITHM_RSA_PAD_ISO9796	0x00000008
-#define SC_ALGORITHM_RSA_PAD_PSS	0x00000010
+#define SC_ALGORITHM_RSA_PAD_PSS	0x00000010 /* PKCS#1 v2.0 PSS */
 
 /* If the card is willing to produce a cryptogram with the following
- * hash values, set these flags accordingly. */
-#define SC_ALGORITHM_RSA_HASH_NONE	0x00000100
+ * hash values, set these flags accordingly.  The interpretation of the hash
+ * flags depends on the algorithm and padding chosen: for RSA, the hash flags
+ * determine how the padding is constructed and do not describe the first
+ * hash applied to the document before padding begins.
+ *
+ *   - For PAD_NONE, ANSI X9.31, (and ISO9796?), the hash value is therefore
+ *     ignored.  For ANSI X9.31, the input data must already have the hash
+ *     identifier byte appended (eg 0x33 for SHA-1).
+ *   - For PKCS1 (v1.5) the hash is recorded in the padding, and HASH_NONE is a
+ *     valid value, meaning that the hash's DigestInfo has already been
+ *     prepended to the data, otherwise the hash id is put on the front.
+ *   - For PSS (PKCS#1 v2.0) the hash is used to derive the padding from the
+ *     already-hashed message.
+ *
+ * In no case is the hash actually applied to the entire document.
+ *
+ * It's possible that the card may support different hashes for PKCS1 and PSS
+ * signatures; in this case the card driver has to pick the lowest-denominator
+ * when it sets these flags to indicate its capabilities. */
+#define SC_ALGORITHM_RSA_HASH_NONE	0x00000100 /* only applies to PKCS1 padding */
 #define SC_ALGORITHM_RSA_HASH_SHA1	0x00000200
 #define SC_ALGORITHM_RSA_HASH_MD5	0x00000400
 #define SC_ALGORITHM_RSA_HASH_MD5_SHA1	0x00000800
@@ -114,21 +134,39 @@ extern "C" {
 #define SC_ALGORITHM_RSA_HASH_SHA384	0x00004000
 #define SC_ALGORITHM_RSA_HASH_SHA512	0x00008000
 #define SC_ALGORITHM_RSA_HASH_SHA224	0x00010000
-#define SC_ALGORITHM_RSA_HASHES		0x0001FE00
-
+#define SC_ALGORITHM_RSA_HASHES		0x0001FF00
+
+/* This defines the hashes to be used with MGF1 in PSS padding */
+#define SC_ALGORITHM_MGF1_SHA1		0x00100000
+#define SC_ALGORITHM_MGF1_SHA256	0x00200000
+#define SC_ALGORITHM_MGF1_SHA384	0x00400000
+#define SC_ALGORITHM_MGF1_SHA512	0x00800000
+#define SC_ALGORITHM_MGF1_SHA224	0x01000000
+#define SC_ALGORITHM_MGF1_HASHES	0x01F00000
+
+/* These flags are exclusive: a GOST R34.10 card must support at least one or the
+ * other of the methods, and exactly one of them applies to any given operation.
+ * Note that the GOST R34.11 hash is actually applied to the data (ie if this
+ * algorithm is chosen the entire unhashed document is passed in). */
 #define SC_ALGORITHM_GOSTR3410_RAW		0x00020000
-#define SC_ALGORITHM_GOSTR3410_HASH_NONE	0x00040000
+#define SC_ALGORITHM_GOSTR3410_HASH_NONE	SC_ALGORITHM_GOSTR3410_RAW /*XXX*/
 #define SC_ALGORITHM_GOSTR3410_HASH_GOSTR3411	0x00080000
-#define SC_ALGORITHM_GOSTR3410_HASHES		0x00080000
-/*TODO: -DEE Should the above be 0x0000E000 */
-/* Or should the HASH_NONE be 0x00000010  and HASHES be 0x00008010 */
-
+#define SC_ALGORITHM_GOSTR3410_HASHES		0x000A0000
+/*TODO: -DEE Should the above be 0x000E0000 */
+/* Or should the HASH_NONE be 0x00000100  and HASHES be 0x00080010 */
+
+/* The ECDSA flags are exclusive, and exactly one of them applies to any given
+ * operation.  If ECDSA with a hash is specified, then the data passed in is
+ * the entire document, unhashed, and the hash is applied once to it before
+ * truncating and signing.  These flags are distinct from the RSA hash flags,
+ * which determine the hash ids the card is willing to put in RSA message
+ * padding. */
 /* May need more bits if card can do more hashes */
 /* TODO: -DEE Will overload RSA_HASHES with EC_HASHES */
 /* Not clear if these need their own bits or not */
 /* The PIV card does not support and hashes */
-#define SC_ALGORITHM_ECDSA_RAW		0x00100000
 #define SC_ALGORITHM_ECDH_CDH_RAW	0x00200000
+#define SC_ALGORITHM_ECDSA_RAW		0x00100000
 #define SC_ALGORITHM_ECDSA_HASH_NONE		SC_ALGORITHM_RSA_HASH_NONE
 #define SC_ALGORITHM_ECDSA_HASH_SHA1		SC_ALGORITHM_RSA_HASH_SHA1
 #define SC_ALGORITHM_ECDSA_HASH_SHA224		SC_ALGORITHM_RSA_HASH_SHA224
@@ -142,7 +180,9 @@ extern "C" {
 							SC_ALGORITHM_ECDSA_HASH_SHA512)
 
 /* define mask of all algorithms that can do raw */
-#define SC_ALGORITHM_RAW_MASK (SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_GOSTR3410_RAW | SC_ALGORITHM_ECDSA_RAW)
+#define SC_ALGORITHM_RAW_MASK (SC_ALGORITHM_RSA_RAW | \
+                               SC_ALGORITHM_GOSTR3410_RAW | \
+                               SC_ALGORITHM_ECDSA_RAW)
 
 /* extended algorithm bits for selected mechs */
 #define SC_ALGORITHM_EXT_EC_F_P          0x00000001
diff --git a/src/libopensc/padding.c b/src/libopensc/padding.c
index f544e5778..53a87c352 100644
--- a/src/libopensc/padding.c
+++ b/src/libopensc/padding.c
@@ -23,6 +23,12 @@
 #include "config.h"
 #endif
 
+#ifdef ENABLE_OPENSSL
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/sha.h>
+#endif
+
 #include <string.h>
 #include <stdlib.h>
 
@@ -231,22 +237,183 @@ int sc_pkcs1_strip_digest_info_prefix(unsigned int *algorithm,
 	return SC_ERROR_INTERNAL;
 }
 
+#ifdef ENABLE_OPENSSL
+
+static const EVP_MD* hash_flag2md(unsigned int hash)
+{
+	switch (hash & SC_ALGORITHM_RSA_HASHES) {
+	case SC_ALGORITHM_RSA_HASH_SHA1:
+		return EVP_sha1();
+	case SC_ALGORITHM_RSA_HASH_SHA224:
+		return EVP_sha224();
+	case SC_ALGORITHM_RSA_HASH_SHA256:
+		return EVP_sha256();
+	case SC_ALGORITHM_RSA_HASH_SHA384:
+		return EVP_sha384();
+	case SC_ALGORITHM_RSA_HASH_SHA512:
+		return EVP_sha512();
+	default:
+		return NULL;
+	}
+}
+
+static const EVP_MD* mgf1_flag2md(unsigned int mgf1)
+{
+	switch (mgf1 & SC_ALGORITHM_MGF1_HASHES) {
+	case SC_ALGORITHM_MGF1_SHA1:
+		return EVP_sha1();
+	case SC_ALGORITHM_MGF1_SHA224:
+		return EVP_sha224();
+	case SC_ALGORITHM_MGF1_SHA256:
+		return EVP_sha256();
+	case SC_ALGORITHM_MGF1_SHA384:
+		return EVP_sha384();
+	case SC_ALGORITHM_MGF1_SHA512:
+		return EVP_sha512();
+	default:
+		return NULL;
+	}
+}
+
+/* add PKCS#1 v2.0 PSS padding */
+static int sc_pkcs1_add_pss_padding(unsigned int hash, unsigned int mgf1_hash,
+    const u8 *in, size_t in_len, u8 *out, size_t *out_len, size_t mod_bits)
+{
+	/* hLen = sLen in our case */
+	int rv = SC_ERROR_INTERNAL, i, j, hlen, dblen, plen, round, mgf_rounds;
+	int mgf1_hlen;
+	const EVP_MD* md, *mgf1_md;
+	EVP_MD_CTX* ctx = NULL;
+	u8 buf[8];
+	u8 salt[EVP_MAX_MD_SIZE], mask[EVP_MAX_MD_SIZE];
+	size_t mod_length = (mod_bits + 7) / 8;
+
+	if (*out_len < mod_length)
+		return SC_ERROR_BUFFER_TOO_SMALL;
+
+	md = hash_flag2md(hash);
+	if (md == NULL)
+		return SC_ERROR_NOT_SUPPORTED;
+	hlen = EVP_MD_size(md);
+	dblen = mod_length - hlen - 1; /* emLen - hLen - 1 */
+	plen = mod_length - 2*hlen - 1;
+	if (in_len != (unsigned)hlen)
+		return SC_ERROR_INVALID_ARGUMENTS;
+	if (2 * (unsigned)hlen + 2 > mod_length)
+		/* RSA key too small for chosen hash (1296 bits or higher needed for
+		 * signing SHA-512 hashes) */
+		return SC_ERROR_NOT_SUPPORTED;
+
+	if (RAND_bytes(salt, hlen) != 1)
+		return SC_ERROR_INTERNAL;
+
+	/* Hash M' to create H */
+	if (!(ctx = EVP_MD_CTX_create()))
+		goto done;
+	memset(buf, 0x00, 8);
+	if (EVP_DigestInit_ex(ctx, md, NULL) != 1 ||
+	    EVP_DigestUpdate(ctx, buf, 8) != 1 ||
+	    EVP_DigestUpdate(ctx, in, hlen) != 1 || /* mHash */
+	    EVP_DigestUpdate(ctx, salt, hlen) != 1) {
+		goto done;
+	}
+
+	/* Construct padding2, salt, H, and BC in the output block */
+	/* DB = PS || 0x01 || salt */
+	memset(out, 0x00, plen - 1); /* emLen - sLen - hLen - 2 */
+	out[plen - 1] = 0x01;
+	memcpy(out + plen, salt, hlen);
+	if (EVP_DigestFinal_ex(ctx, out + dblen, NULL) != 1) { /* H */
+		goto done;
+	}
+	out[dblen + hlen] = 0xBC;
+	/* EM = DB* || H || 0xbc
+	 *  *the first part is masked later */
+
+	/* Construct the DB mask block by block and XOR it in. */
+	mgf1_md = mgf1_flag2md(mgf1_hash);
+	if (mgf1_md == NULL)
+		return SC_ERROR_NOT_SUPPORTED;
+	mgf1_hlen = EVP_MD_size(mgf1_md);
+
+	mgf_rounds = (dblen + mgf1_hlen - 1) / mgf1_hlen; /* round up */
+	for (round = 0; round < mgf_rounds; ++round) {
+		buf[0] = (round&0xFF000000U) >> 24;
+		buf[1] = (round&0x00FF0000U) >> 16;
+		buf[2] = (round&0x0000FF00U) >> 8;
+		buf[3] = (round&0x000000FFU);
+		if (EVP_DigestInit_ex(ctx, mgf1_md, NULL) != 1 ||
+		    EVP_DigestUpdate(ctx, out + dblen, hlen) != 1 || /* H (Z parameter of MGF1) */
+		    EVP_DigestUpdate(ctx, buf, 4) != 1 || /* C */
+		    EVP_DigestFinal_ex(ctx, mask, NULL)) {
+			goto done;
+		}
+		/* this is no longer part of the MGF1, but actually
+		 * XORing mask with DB to create maskedDB inplace */
+		for (i = round * mgf1_hlen, j = 0; i < dblen && j < mgf1_hlen; ++i, ++j) {
+			out[i] ^= mask[j];
+		}
+	}
+
+	/* Set leftmost N bits in leftmost octet in maskedDB to zero
+	 * to make sure the result is smaller than the modulus ( +1)
+	 */
+	out[0] &= (0xff >> (8 * mod_length - mod_bits + 1));
+
+	*out_len = mod_length;
+	rv = SC_SUCCESS;
+
+done:
+	OPENSSL_cleanse(salt, sizeof(salt));
+	OPENSSL_cleanse(mask, sizeof(mask));
+	if (ctx) {
+		EVP_MD_CTX_destroy(ctx);
+	}
+	return rv;
+}
+
+static int hash_len2algo(size_t hash_len)
+{
+	switch (hash_len) {
+	case SHA_DIGEST_LENGTH:
+		return SC_ALGORITHM_RSA_HASH_SHA1;
+	case SHA224_DIGEST_LENGTH:
+		return SC_ALGORITHM_RSA_HASH_SHA224;
+	case SHA256_DIGEST_LENGTH:
+		return SC_ALGORITHM_RSA_HASH_SHA256;
+	case SHA384_DIGEST_LENGTH:
+		return SC_ALGORITHM_RSA_HASH_SHA384;
+	case SHA512_DIGEST_LENGTH:
+		return SC_ALGORITHM_RSA_HASH_SHA512;
+	}
+	/* Should never happen -- the mechanism and data should be already
+	 * verified to match one of the above. If not, we will fail later
+	 */
+	return SC_ALGORITHM_RSA_HASH_NONE;
+}
+#endif
+
 /* general PKCS#1 encoding function */
 int sc_pkcs1_encode(sc_context_t *ctx, unsigned long flags,
-	const u8 *in, size_t in_len, u8 *out, size_t *out_len, size_t mod_len)
+	const u8 *in, size_t in_len, u8 *out, size_t *out_len, size_t mod_bits)
 {
 	int    rv, i;
 	size_t tmp_len = *out_len;
 	const u8    *tmp = in;
 	unsigned int hash_algo, pad_algo;
+	size_t mod_len = (mod_bits + 7) / 8;
+#ifdef ENABLE_OPENSSL
+	unsigned int mgf1_hash;
+#endif
 
 	LOG_FUNC_CALLED(ctx);
 
-	hash_algo = flags & (SC_ALGORITHM_RSA_HASHES | SC_ALGORITHM_RSA_HASH_NONE);
+	hash_algo = flags & SC_ALGORITHM_RSA_HASHES;
 	pad_algo  = flags & SC_ALGORITHM_RSA_PADS;
 	sc_log(ctx, "hash algorithm 0x%X, pad algorithm 0x%X", hash_algo, pad_algo);
 
-	if (hash_algo != SC_ALGORITHM_RSA_HASH_NONE) {
+	if ((pad_algo == SC_ALGORITHM_RSA_PAD_PKCS1 || !pad_algo) &&
+	    hash_algo != SC_ALGORITHM_RSA_HASH_NONE) {
 		i = sc_pkcs1_add_digest_info_prefix(hash_algo, in, in_len, out, &tmp_len);
 		if (i != SC_SUCCESS) {
 			sc_log(ctx, "Unable to add digest info 0x%x", hash_algo);
@@ -268,10 +435,29 @@ int sc_pkcs1_encode(sc_context_t *ctx, unsigned long flags,
 		/* add pkcs1 bt01 padding */
 		rv = sc_pkcs1_add_01_padding(tmp, tmp_len, out, out_len, mod_len);
 		LOG_FUNC_RETURN(ctx, rv);
+	case SC_ALGORITHM_RSA_PAD_PSS:
+		/* add PSS padding */
+#ifdef ENABLE_OPENSSL
+		mgf1_hash = flags & SC_ALGORITHM_MGF1_HASHES;
+		if (hash_algo == SC_ALGORITHM_RSA_HASH_NONE) {
+			/* this is generic RSA_PKCS1_PSS mechanism with hash
+			 * already done outside of the module. The parameters
+			 * were already checked so we need to adjust the hash
+			 * algorithm to do the padding with the correct hash
+			 * function.
+			 */
+			hash_algo = hash_len2algo(tmp_len);
+		}
+		rv = sc_pkcs1_add_pss_padding(hash_algo, mgf1_hash,
+		    tmp, tmp_len, out, out_len, mod_bits);
+#else
+		rv = SC_ERROR_NOT_SUPPORTED;
+#endif
+		LOG_FUNC_RETURN(ctx, rv);
 	default:
-		/* currently only pkcs1 padding is supported */
-		sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Unsupported padding algorithm 0x%x", pad_algo);
-		LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED);
+		/* We shouldn't be called with an unexpected padding type, we've already
+		 * returned SC_ERROR_NOT_SUPPORTED if the card can't be used. */
+		LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL);
 	}
 }
 
@@ -279,42 +465,45 @@ int sc_get_encoding_flags(sc_context_t *ctx,
 	unsigned long iflags, unsigned long caps,
 	unsigned long *pflags, unsigned long *sflags)
 {
-	size_t i;
-
 	LOG_FUNC_CALLED(ctx);
 	if (pflags == NULL || sflags == NULL)
 		LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
 
 	sc_log(ctx, "iFlags 0x%lX, card capabilities 0x%lX", iflags, caps);
-	for (i = 0; digest_info_prefix[i].algorithm != 0; i++) {
-		if (iflags & digest_info_prefix[i].algorithm) {
-			if (digest_info_prefix[i].algorithm != SC_ALGORITHM_RSA_HASH_NONE &&
-			    caps & digest_info_prefix[i].algorithm)
-				*sflags |= digest_info_prefix[i].algorithm;
-			else
-				*pflags |= digest_info_prefix[i].algorithm;
-			break;
-		}
-	}
 
-	if (iflags & SC_ALGORITHM_RSA_PAD_PKCS1) {
-		if (caps & SC_ALGORITHM_RSA_PAD_PKCS1)
-			*sflags |= SC_ALGORITHM_RSA_PAD_PKCS1;
-		else
-			*pflags |= SC_ALGORITHM_RSA_PAD_PKCS1;
-	} else if ((iflags & SC_ALGORITHM_RSA_PADS) == SC_ALGORITHM_RSA_PAD_NONE) {
-		
-		/* Work with RSA, EC and maybe GOSTR? */
-		if (!(caps & SC_ALGORITHM_RAW_MASK))
-			LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "raw encryption is not supported");
+	/* For ECDSA and GOSTR, we don't do any padding or hashing ourselves, the
+	 * card has to support the requested operation.  Similarly, for RSA with
+	 * raw padding (raw RSA) and ISO9796, we require the card to do it for us.
+	 * Finally, for PKCS1 (v1.5 and PSS) and ASNI X9.31 we can apply the padding
+	 * ourselves if the card supports raw RSA. */
 
-		*sflags |= (caps & SC_ALGORITHM_RAW_MASK); /* adds in the one raw type */
+	/* TODO: Could convert GOSTR3410_HASH_GOSTR3411 -> GOSTR3410_RAW and
+	 *       ECDSA_HASH_ -> ECDSA_RAW using OpenSSL (not much benefit though). */
+
+	if ((caps & iflags) == iflags) {
+		/* Card supports the signature operation we want to do, great, let's
+		 * go with it then. */
+		*sflags = iflags;
 		*pflags = 0;
-	} else if (iflags & SC_ALGORITHM_RSA_PAD_PSS) {
-		if (caps & SC_ALGORITHM_RSA_PAD_PSS)
-			*sflags |= SC_ALGORITHM_RSA_PAD_PSS;
-		else
-			*pflags |= SC_ALGORITHM_RSA_PAD_PSS;
+
+	} else if ((caps & SC_ALGORITHM_RSA_PAD_PSS) &&
+                   (iflags & SC_ALGORITHM_RSA_PAD_PSS)) {
+		*sflags |= SC_ALGORITHM_RSA_PAD_PSS;
+
+	} else if (((caps & SC_ALGORITHM_RSA_RAW) &&
+	            (iflags & SC_ALGORITHM_RSA_PAD_PKCS1))
+	           || iflags & SC_ALGORITHM_RSA_PAD_PSS) {
+		/* Use the card's raw RSA capability on the padded input */
+		*sflags = SC_ALGORITHM_RSA_PAD_NONE;
+		*pflags = iflags;
+
+	} else if ((caps & (SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE)) &&
+	           (iflags & SC_ALGORITHM_RSA_PAD_PKCS1)) {
+		/* A corner case - the card can partially do PKCS1, if we prepend the
+		 * DigestInfo bit it will do the rest. */
+		*sflags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE;
+		*pflags = iflags & SC_ALGORITHM_RSA_HASHES;
+
 	} else {
 		LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "unsupported algorithm");
 	}
diff --git a/src/libopensc/pkcs15-sec.c b/src/libopensc/pkcs15-sec.c
index 6ee4fa3c7..3e7e03b12 100644
--- a/src/libopensc/pkcs15-sec.c
+++ b/src/libopensc/pkcs15-sec.c
@@ -329,7 +329,7 @@ int sc_pkcs15_compute_signature(struct sc_pkcs15_card *p15card,
 
 	switch (obj->type) {
 		case SC_PKCS15_TYPE_PRKEY_RSA:
-			modlen = prkey->modulus_length / 8;
+			modlen = (prkey->modulus_length + 7) / 8;
 			break;
 		case SC_PKCS15_TYPE_PRKEY_GOSTR3410:
 			modlen = (prkey->modulus_length + 7) / 8 * 2;
@@ -377,7 +377,8 @@ int sc_pkcs15_compute_signature(struct sc_pkcs15_card *p15card,
 		if (modlen > tmplen)
 			LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "Buffer too small, needs recompile!");
 
-		r = sc_pkcs1_encode(ctx, flags, in, inlen, buf, &tmplen, modlen);
+		/* XXX Assuming RSA key here */
+		r = sc_pkcs1_encode(ctx, flags, in, inlen, buf, &tmplen, prkey->modulus_length);
 
 		/* no padding needed - already done */
 		flags &= ~SC_ALGORITHM_RSA_PADS;
@@ -391,10 +392,15 @@ int sc_pkcs15_compute_signature(struct sc_pkcs15_card *p15card,
 	}
 
 
-	/* If the card doesn't support the requested algorithm, see if we
-	 * can strip the input so a more restrictive algo can be used */
+	/* If the card doesn't support the requested algorithm, we normally add the
+	 * padding here in software and ask the card to do a raw signature.  There's
+	 * one exception to that, where we might be able to get the signature to
+	 * succeed by stripping padding if the card only offers higher-level
+	 * signature operations.  The only thing we can strip is the DigestInfo
+	 * block from PKCS1 padding. */
 	if ((flags == (SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE)) &&
-			!(alg_info->flags & (SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_HASH_NONE))) {
+	    !(alg_info->flags & SC_ALGORITHM_RSA_RAW) &&
+	    !(alg_info->flags & (SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE))) {
 		unsigned int algo;
 		size_t tmplen = sizeof(buf);
 
@@ -420,19 +426,16 @@ int sc_pkcs15_compute_signature(struct sc_pkcs15_card *p15card,
 
 	/* add the padding bytes (if necessary) */
 	if (pad_flags != 0) {
-		if (flags & SC_ALGORITHM_RSA_PAD_PSS) {
-			// TODO PSS padding
-		} else {
-			size_t tmplen = sizeof(buf);
-
-			r = sc_pkcs1_encode(ctx, pad_flags, tmp, inlen, tmp, &tmplen, modlen);
-			SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to add padding");
+		size_t tmplen = sizeof(buf);
 
-			inlen = tmplen;
-		}
+		/* XXX Assuming RSA key here */
+		r = sc_pkcs1_encode(ctx, pad_flags, tmp, inlen, tmp, &tmplen,
+		    prkey->modulus_length);
+		SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to add padding");
+		inlen = tmplen;
 	}
 	else if ( senv.algorithm == SC_ALGORITHM_RSA &&
-			(flags & SC_ALGORITHM_RSA_PADS) == SC_ALGORITHM_RSA_PAD_NONE) {
+	          (flags & SC_ALGORITHM_RSA_PADS) == SC_ALGORITHM_RSA_PAD_NONE) {
 		/* Add zero-padding if input is shorter than the modulus */
 		if (inlen < modlen) {
 			if (modlen > sizeof(buf))
diff --git a/src/pkcs11/framework-pkcs15.c b/src/pkcs11/framework-pkcs15.c
index 80f9ce89f..a75d239f4 100644
--- a/src/pkcs11/framework-pkcs15.c
+++ b/src/pkcs11/framework-pkcs15.c
@@ -3478,7 +3478,8 @@ struct sc_pkcs11_object_ops pkcs15_cert_ops = {
 	NULL,	/* unwrap_key */
 	NULL,	/* decrypt */
 	NULL,	/* derive */
-	NULL	/* can_do */
+	NULL,	/* can_do */
+	NULL	/* init_params */
 };
 
 /*
@@ -3703,53 +3704,44 @@ static CK_RV
 pkcs15_prkey_check_pss_param(CK_MECHANISM_PTR pMechanism, CK_ULONG hlen)
 {
 	CK_RSA_PKCS_PSS_PARAMS *pss_param;
-
-	if (pMechanism->pParameter == NULL)
-		return CKR_OK;				// Support applications that don't provide CK_RSA_PKCS_PSS_PARAMS
-
-	if (pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS))
-		return CKR_MECHANISM_PARAM_INVALID;
+	int i;
+	const unsigned int hash_lens[5] = { 160, 256, 385, 512, 224 };
+	const unsigned int hashes[5] = { CKM_SHA_1, CKM_SHA256,
+		CKM_SHA384, CKM_SHA512, CKM_SHA224 };
 
 	pss_param = (CK_RSA_PKCS_PSS_PARAMS *)pMechanism->pParameter;
 
-	// Hash parameter must match mechanisms or length of data supplied for CKM_RSA_PKCS_PSS
-	switch(pss_param->hashAlg) {
-	case CKM_SHA_1:
-		if (hlen != 20)
-			return CKR_MECHANISM_PARAM_INVALID;
-		break;
-	case CKM_SHA256:
-		if (hlen != 32)
+	// Hash parameter must match length of data supplied for CKM_RSA_PKCS_PSS
+	for (i = 0; i < 5; i++) {
+		if (pss_param->hashAlg == hashes[i]
+		    && hlen != hash_lens[i]/8)
 			return CKR_MECHANISM_PARAM_INVALID;
-		break;
-	default:
-		return CKR_MECHANISM_PARAM_INVALID;
 	}
+	/* other aspects of pss params were already verified during SignInit */
 
-	// SmartCards typically only support MGFs based on the same hash as the
-	// message digest
-	switch(pss_param->mgf) {
-	case CKG_MGF1_SHA1:
-		if (hlen != 20)
-			return CKR_MECHANISM_PARAM_INVALID;
+	return CKR_OK;
+}
+
+static int mgf2flags(CK_RSA_PKCS_MGF_TYPE mgf)
+{
+	switch (mgf) {
+	case CKG_MGF1_SHA224:
+		return SC_ALGORITHM_MGF1_SHA224;
 		break;
 	case CKG_MGF1_SHA256:
-		if (hlen != 32)
-			return CKR_MECHANISM_PARAM_INVALID;
-		break;
+		return SC_ALGORITHM_MGF1_SHA256;
+	case CKG_MGF1_SHA384:
+		return SC_ALGORITHM_MGF1_SHA384;
+	case CKG_MGF1_SHA512:
+		return SC_ALGORITHM_MGF1_SHA512;
+	case CKG_MGF1_SHA1:
+		return SC_ALGORITHM_MGF1_SHA1;
 	default:
-		return CKR_MECHANISM_PARAM_INVALID;
+		return -1;
 	}
-
-	// SmartCards typically support only a salt length equal to the hash length
-	if (pss_param->sLen != hlen)
-		return CKR_MECHANISM_PARAM_INVALID;
-
-	return CKR_OK;
 }
 
 
-
 static CK_RV
 pkcs15_prkey_sign(struct sc_pkcs11_session *session, void *obj,
 			CK_MECHANISM_PTR pMechanism, CK_BYTE_PTR pData,
@@ -3798,35 +3790,74 @@ pkcs15_prkey_sign(struct sc_pkcs11_session *session, void *obj,
 	case CKM_SHA512_RSA_PKCS:
 		flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA512;
 		break;
+	case CKM_RIPEMD160_RSA_PKCS:
+		flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_RIPEMD160;
+		break;
+	case CKM_RSA_X_509:
+		flags = SC_ALGORITHM_RSA_RAW;
+		break;
 	case CKM_RSA_PKCS_PSS:
-		rv = pkcs15_prkey_check_pss_param(pMechanism, (int)ulDataLen);
+		flags = SC_ALGORITHM_RSA_PAD_PSS;
+		/* The hash was done ouside of the module */
+		flags |= SC_ALGORITHM_RSA_HASH_NONE;
+		/* Omited parameter can use MGF1-SHA1 ? */
+		if (pMechanism->pParameter == NULL) {
+			flags |= SC_ALGORITHM_MGF1_SHA1;
+			if (ulDataLen != SHA_DIGEST_LENGTH)
+				return CKR_MECHANISM_PARAM_INVALID;
+			break;
+		}
 
-		if (rv != CKR_OK)
+		/* Check the data length matches the selected hash */
+		rv = pkcs15_prkey_check_pss_param(pMechanism, (int)ulDataLen);
+		if (rv != CKR_OK) {
+			sc_log(context, "Invalid data lenght for the selected "
+			    "PSS parameters");
 			return rv;
+		}
 
-		flags = SC_ALGORITHM_RSA_PAD_PSS | SC_ALGORITHM_RSA_HASH_NONE;
-		break;
-	case CKM_SHA1_RSA_PKCS_PSS:
-		rv = pkcs15_prkey_check_pss_param(pMechanism, 20);
-
-		if (rv != CKR_OK)
-			return rv;
+		/* The MGF parameter was already verified in SignInit() */
+		flags |=  mgf2flags(((CK_RSA_PKCS_PSS_PARAMS*)pMechanism->pParameter)->mgf);
 
-		flags = SC_ALGORITHM_RSA_PAD_PSS | SC_ALGORITHM_RSA_HASH_SHA1;
+		/* Assuming salt is the size of hash */
 		break;
+	case CKM_SHA1_RSA_PKCS_PSS:
+	case CKM_SHA224_RSA_PKCS_PSS:
 	case CKM_SHA256_RSA_PKCS_PSS:
-		rv = pkcs15_prkey_check_pss_param(pMechanism, 32);
+	case CKM_SHA384_RSA_PKCS_PSS:
+	case CKM_SHA512_RSA_PKCS_PSS:
+		flags = SC_ALGORITHM_RSA_PAD_PSS;
+		/* Omited parameter can use MGF1-SHA1 and SHA1 hash ? */
+		if (pMechanism->pParameter == NULL) {
+			flags |= SC_ALGORITHM_RSA_HASH_SHA1;
+			flags |= SC_ALGORITHM_MGF1_SHA1;
+			break;
+		}
 
-		if (rv != CKR_OK)
-			return rv;
+		switch (((CK_RSA_PKCS_PSS_PARAMS*)pMechanism->pParameter)->hashAlg) {
+		case CKM_SHA_1:
+			flags |= SC_ALGORITHM_RSA_HASH_SHA1;
+			break;
+		case CKM_SHA224:
+			flags |= SC_ALGORITHM_RSA_HASH_SHA224;
+			break;
+		case CKM_SHA256:
+			flags |= SC_ALGORITHM_RSA_HASH_SHA256;
+			break;
+		case CKM_SHA384:
+			flags |= SC_ALGORITHM_RSA_HASH_SHA384;
+			break;
+		case CKM_SHA512:
+			flags |= SC_ALGORITHM_RSA_HASH_SHA512;
+			break;
+		default:
+			return CKR_MECHANISM_PARAM_INVALID;
+		}
 
-		flags = SC_ALGORITHM_RSA_PAD_PSS | SC_ALGORITHM_RSA_HASH_SHA256;
-		break;
-	case CKM_RIPEMD160_RSA_PKCS:
-		flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_RIPEMD160;
-		break;
-	case CKM_RSA_X_509:
-		flags = SC_ALGORITHM_RSA_RAW;
+		/* The MGF parameter was already verified in SignInit() */
+		flags |= mgf2flags(((CK_RSA_PKCS_PSS_PARAMS*)pMechanism->pParameter)->mgf);
+
+		/* Assuming salt is the size of hash */
 		break;
 	case CKM_GOSTR3410:
 		flags = SC_ALGORITHM_GOSTR3410_HASH_NONE;
@@ -4074,6 +4105,76 @@ pkcs15_prkey_can_do(struct sc_pkcs11_session *session, void *obj,
 }
 
 
+static CK_RV
+pkcs15_prkey_init_params(struct sc_pkcs11_session *session,
+			CK_MECHANISM_PTR pMechanism)
+{
+	const CK_RSA_PKCS_PSS_PARAMS *pss_params;
+	unsigned int expected_hash = 0, i;
+	unsigned int expected_salt_len = 0;
+	const unsigned int salt_lens[5] = { 160, 256, 384, 512, 224 };
+	const unsigned int hashes[5] = { CKM_SHA_1, CKM_SHA256,
+		CKM_SHA384, CKM_SHA512, CKM_SHA224 };
+
+	switch (pMechanism->mechanism) {
+	case CKM_RSA_PKCS_PSS:
+	case CKM_SHA1_RSA_PKCS_PSS:
+	case CKM_SHA224_RSA_PKCS_PSS:
+	case CKM_SHA256_RSA_PKCS_PSS:
+	case CKM_SHA384_RSA_PKCS_PSS:
+	case CKM_SHA512_RSA_PKCS_PSS:
+		if (!pMechanism->pParameter ||
+		    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS))
+			return CKR_MECHANISM_PARAM_INVALID;
+
+		pss_params = (CK_RSA_PKCS_PSS_PARAMS*)pMechanism->pParameter;
+		if (pss_params->mgf < CKG_MGF1_SHA1 || pss_params->mgf > CKG_MGF1_SHA224)
+			return CKR_MECHANISM_PARAM_INVALID;
+
+		/* The hashAlg field can have any value for CKM_RSA_PKCS_PSS and must be
+		 * used again in the PSS padding; for the other mechanisms it strictly
+		 * must match the padding declared in the mechanism.
+		 */
+		if (pMechanism->mechanism == CKM_SHA1_RSA_PKCS_PSS) {
+			expected_hash = CKM_SHA_1;
+			expected_salt_len = 160;
+		} else if (pMechanism->mechanism == CKM_SHA224_RSA_PKCS_PSS) {
+			expected_hash = CKM_SHA224;
+			expected_salt_len = 224;
+		} else if (pMechanism->mechanism == CKM_SHA256_RSA_PKCS_PSS) {
+			expected_hash = CKM_SHA256;
+			expected_salt_len = 256;
+		} else if (pMechanism->mechanism == CKM_SHA384_RSA_PKCS_PSS) {
+			expected_hash = CKM_SHA384;
+			expected_salt_len = 384;
+		} else if (pMechanism->mechanism == CKM_SHA512_RSA_PKCS_PSS) {
+			expected_hash = CKM_SHA512;
+			expected_salt_len = 512;
+		} else if (pMechanism->mechanism == CKM_RSA_PKCS_PSS) {
+			for (i = 0; i < 5; ++i) {
+			        if (hashes[i] == pss_params->hashAlg) {
+			                expected_hash = hashes[i];
+			                expected_salt_len = salt_lens[i];
+				}
+			}
+		}
+
+		if (expected_hash != pss_params->hashAlg)
+			return CKR_MECHANISM_PARAM_INVALID;
+
+		/* We're strict, and only do PSS signatures with a salt length that
+		 * matches the digest length (any shorter is rubbish, any longer
+		 * is useless). */
+		if (pss_params->sLen != expected_salt_len / 8)
+			return CKR_MECHANISM_PARAM_INVALID;
+
+		/* TODO support different salt lengths */
+		break;
+	}
+	return CKR_OK;
+}
+
+
 struct sc_pkcs11_object_ops pkcs15_prkey_ops = {
 	pkcs15_prkey_release,
 	pkcs15_prkey_set_attribute,
@@ -4084,8 +4185,9 @@ struct sc_pkcs11_object_ops pkcs15_prkey_ops = {
 	pkcs15_prkey_sign,
 	NULL,	/* unwrap */
 	pkcs15_prkey_decrypt,
-        pkcs15_prkey_derive,
-        pkcs15_prkey_can_do
+	pkcs15_prkey_derive,
+	pkcs15_prkey_can_do,
+	pkcs15_prkey_init_params,
 };
 
 /*
@@ -4322,7 +4424,8 @@ struct sc_pkcs11_object_ops pkcs15_pubkey_ops = {
 	NULL,	/* unwrap_key */
 	NULL,	/* decrypt */
 	NULL,	/* derive */
-	NULL	/* can_do */
+	NULL,	/* can_do */
+	NULL	/* init_params */
 };
 
 
@@ -4500,7 +4603,8 @@ struct sc_pkcs11_object_ops pkcs15_dobj_ops = {
 	NULL,	/* unwrap_key */
 	NULL,	/* decrypt */
 	NULL,	/* derive */
-	NULL	/* can_do */
+	NULL,	/* can_do */
+	NULL	/* init_params */
 };
 
 
@@ -4629,7 +4733,8 @@ struct sc_pkcs11_object_ops pkcs15_skey_ops = {
 	NULL,	/* unwrap_key */
 	NULL,	/* decrypt */
 	NULL,	/* derive */
-	NULL	/* can_do */
+	NULL,	/* can_do */
+	NULL	/* init_params */
 };
 
 /*
@@ -5040,6 +5145,17 @@ register_mechanisms(struct sc_pkcs11_card *p11card)
 		/* We support PKCS1 padding in software */
 		/* either the card supports it or OpenSC does */
 		rsa_flags |= SC_ALGORITHM_RSA_PAD_PKCS1;
+#ifdef ENABLE_OPENSSL
+		rsa_flags |= SC_ALGORITHM_RSA_PAD_PSS;
+#endif
+	}
+
+	if (rsa_flags & SC_ALGORITHM_RSA_PAD_ISO9796) {
+		/* Supported in hardware only, if the card driver declares it. */
+		mt = sc_pkcs11_new_fw_mechanism(CKM_RSA_9796, &mech_info, CKK_RSA, NULL, NULL);
+		rc = sc_pkcs11_register_mechanism(p11card, mt);
+		if (rc != CKR_OK)
+			return rc;
 	}
 
 #ifdef ENABLE_OPENSSL
@@ -5098,23 +5214,40 @@ register_mechanisms(struct sc_pkcs11_card *p11card)
 #endif /* ENABLE_OPENSSL */
 	}
 
-	/* TODO support other padding mechanisms */
-
 	if (rsa_flags & SC_ALGORITHM_RSA_PAD_PSS) {
-		mech_info.flags &= ~(CKF_DECRYPT|CKF_VERIFY);
-
+		mech_info.flags &= ~(CKF_DECRYPT|CKF_ENCRYPT);
 		mt = sc_pkcs11_new_fw_mechanism(CKM_RSA_PKCS_PSS, &mech_info, CKK_RSA, NULL, NULL);
 		rc = sc_pkcs11_register_mechanism(p11card, mt);
 		if (rc != CKR_OK)
 			return rc;
 
 		if (rsa_flags & SC_ALGORITHM_RSA_HASH_SHA1) {
-			rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_SHA1_RSA_PKCS_PSS, CKM_SHA_1, mt);
+			rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card,
+			    CKM_SHA1_RSA_PKCS_PSS, CKM_SHA_1, mt);
+			if (rc != CKR_OK)
+				return rc;
+		}
+		if (rsa_flags & SC_ALGORITHM_RSA_HASH_SHA224) {
+			rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card,
+			    CKM_SHA224_RSA_PKCS_PSS, CKM_SHA224, mt);
 			if (rc != CKR_OK)
 				return rc;
 		}
 		if (rsa_flags & SC_ALGORITHM_RSA_HASH_SHA256) {
-			rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_SHA256_RSA_PKCS_PSS, CKM_SHA256, mt);
+			rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card,
+			    CKM_SHA256_RSA_PKCS_PSS, CKM_SHA256, mt);
+			if (rc != CKR_OK)
+				return rc;
+		}
+		if (rsa_flags & SC_ALGORITHM_RSA_HASH_SHA384) {
+			rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card,
+			    CKM_SHA384_RSA_PKCS_PSS, CKM_SHA384, mt);
+			if (rc != CKR_OK)
+				return rc;
+		}
+		if (rsa_flags & SC_ALGORITHM_RSA_HASH_SHA512) {
+			rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card,
+			    CKM_SHA512_RSA_PKCS_PSS, CKM_SHA512, mt);
 			if (rc != CKR_OK)
 				return rc;
 		}
diff --git a/src/pkcs11/mechanism.c b/src/pkcs11/mechanism.c
index 5f006c839..d4ce7fef5 100644
--- a/src/pkcs11/mechanism.c
+++ b/src/pkcs11/mechanism.c
@@ -262,11 +262,20 @@ sc_pkcs11_sign_init(struct sc_pkcs11_session *session, CK_MECHANISM_PTR pMechani
 	if (mt->key_type != key_type)
 		LOG_FUNC_RETURN(context, CKR_KEY_TYPE_INCONSISTENT);
 
+	if (pMechanism->pParameter &&
+	    pMechanism->ulParameterLen > sizeof(operation->mechanism_params))
+		LOG_FUNC_RETURN(context, CKR_ARGUMENTS_BAD);
+
 	rv = session_start_operation(session, SC_PKCS11_OPERATION_SIGN, mt, &operation);
 	if (rv != CKR_OK)
 		LOG_FUNC_RETURN(context, rv);
 
 	memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM));
+	if (pMechanism->pParameter) {
+		memcpy(&operation->mechanism_params, pMechanism->pParameter,
+		       pMechanism->ulParameterLen);
+		operation->mechanism.pParameter = &operation->mechanism_params;
+	}
 	rv = mt->sign_init(operation, key);
 	if (rv != CKR_OK)
 		session_stop_operation(session, SC_PKCS11_OPERATION_SIGN);
@@ -387,6 +396,16 @@ sc_pkcs11_signature_init(sc_pkcs11_operation_t *operation,
 		}
 	}
 
+	/* Validate the mechanism parameters */
+	if (key->ops->init_params) {
+		rv = key->ops->init_params(operation->session, &operation->mechanism);
+		if (rv != CKR_OK) {
+			/* Probably bad arguments */
+			free(data);
+			LOG_FUNC_RETURN(context, rv);
+		}
+	}
+
 	/* If this is a signature with hash operation,
 	 * and card cannot perform itself signature with hash operation,
 	 * set up the hash operation */
@@ -636,6 +655,16 @@ sc_pkcs11_verify_init(sc_pkcs11_operation_t *operation,
 		}
 	}
 
+	/* Validate the mechanism parameters */
+	if (key->ops->init_params) {
+		rv = key->ops->init_params(operation->session, &operation->mechanism);
+		if (rv != CKR_OK) {
+			/* Probably bad arguments */
+			free(data);
+			LOG_FUNC_RETURN(context, rv);
+		}
+	}
+
 	/* If this is a verify with hash operation, set up the
 	 * hash operation */
 	info = (struct hash_signature_info *) operation->type->mech_data;
@@ -729,7 +758,7 @@ sc_pkcs11_verify_final(sc_pkcs11_operation_t *operation,
 
 	rv = sc_pkcs11_verify_data(pubkey_value, attr.ulValueLen,
 		params, sizeof(params),
-		operation->mechanism.mechanism, data->md,
+		&operation->mechanism, data->md,
 		data->buffer, data->buffer_len, pSignature, ulSignatureLen);
 
 done:
diff --git a/src/pkcs11/openssl.c b/src/pkcs11/openssl.c
index 59de1210d..e8b246145 100644
--- a/src/pkcs11/openssl.c
+++ b/src/pkcs11/openssl.c
@@ -68,6 +68,23 @@ static sc_pkcs11_mechanism_type_t openssl_sha1_mech = {
 	NULL,			/* free_mech_data */
 };
 
+static sc_pkcs11_mechanism_type_t openssl_sha224_mech = {
+	CKM_SHA224,
+	{ 0, 0, CKF_DIGEST },
+	0,
+	sizeof(struct sc_pkcs11_operation),
+	sc_pkcs11_openssl_md_release,
+	sc_pkcs11_openssl_md_init,
+	sc_pkcs11_openssl_md_update,
+	sc_pkcs11_openssl_md_final,
+	NULL, NULL, NULL, NULL,	/* sign_* */
+	NULL, NULL, NULL,	/* verif_* */
+	NULL, NULL,		/* decrypt_* */
+	NULL,			/* derive */
+	NULL,			/* mech_data */
+	NULL,			/* free_mech_data */
+};
+
 #if OPENSSL_VERSION_NUMBER >= 0x00908000L
 static sc_pkcs11_mechanism_type_t openssl_sha256_mech = {
 	CKM_SHA256,
@@ -231,6 +248,8 @@ sc_pkcs11_register_openssl_mechanisms(struct sc_pkcs11_card *p11card)
 
 	openssl_sha1_mech.mech_data = EVP_sha1();
 	sc_pkcs11_register_mechanism(p11card, dup_mem(&openssl_sha1_mech, sizeof openssl_sha1_mech));
+	openssl_sha224_mech.mech_data = EVP_sha224();
+	sc_pkcs11_register_mechanism(p11card, dup_mem(&openssl_sha224_mech, sizeof openssl_sha224_mech));
 #if OPENSSL_VERSION_NUMBER >= 0x00908000L
 	openssl_sha256_mech.mech_data = EVP_sha256();
 	sc_pkcs11_register_mechanism(p11card, dup_mem(&openssl_sha256_mech, sizeof openssl_sha256_mech));
@@ -396,7 +415,7 @@ static CK_RV gostr3410_verify_data(const unsigned char *pubkey, int pubkey_len,
  */
 CK_RV sc_pkcs11_verify_data(const unsigned char *pubkey, int pubkey_len,
 			const unsigned char *pubkey_params, int pubkey_params_len,
-			CK_MECHANISM_TYPE mech, sc_pkcs11_operation_t *md,
+			CK_MECHANISM_PTR mech, sc_pkcs11_operation_t *md,
 			unsigned char *data, int data_len,
 			unsigned char *signat, int signat_len)
 {
@@ -405,7 +424,7 @@ CK_RV sc_pkcs11_verify_data(const unsigned char *pubkey, int pubkey_len,
 	EVP_PKEY *pkey = NULL;
 	const unsigned char *pubkey_tmp = NULL;
 
-	if (mech == CKM_GOSTR3410)
+	if (mech->mechanism == CKM_GOSTR3410)
 	{
 #if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC)
 		return gostr3410_verify_data(pubkey, pubkey_len,
@@ -429,37 +448,53 @@ CK_RV sc_pkcs11_verify_data(const unsigned char *pubkey, int pubkey_len,
 	if (pkey == NULL)
 		return CKR_GENERAL_ERROR;
 
-	if (md != NULL) {
+	if (md != NULL && (mech->mechanism == CKM_SHA1_RSA_PKCS
+		|| mech->mechanism == CKM_SHA224_RSA_PKCS
+		|| mech->mechanism == CKM_SHA256_RSA_PKCS
+		|| mech->mechanism == CKM_SHA384_RSA_PKCS
+		|| mech->mechanism == CKM_SHA512_RSA_PKCS)) {
 		EVP_MD_CTX *md_ctx = DIGEST_CTX(md);
 
+		/* This does not really use the data argument, but the data
+		 * are already collected in the md_ctx
+		 */
+		sc_log(context, "Trying to verify using EVP");
 		res = EVP_VerifyFinal(md_ctx, signat, signat_len, pkey);
 		EVP_PKEY_free(pkey);
 		if (res == 1)
 			return CKR_OK;
-		else if (res == 0)
+		else if (res == 0) {
+			sc_log(context, "EVP_VerifyFinal(): Signature invalid");
 			return CKR_SIGNATURE_INVALID;
-		else {
+		} else {
 			sc_log(context, "EVP_VerifyFinal() returned %d\n", res);
 			return CKR_GENERAL_ERROR;
 		}
-	}
-	else {
+	} else {
 		RSA *rsa;
 		unsigned char *rsa_out = NULL, pad;
 		int rsa_outlen = 0;
 
-		switch(mech) {
+		sc_log(context, "Trying to verify using low-level API");
+		switch (mech->mechanism) {
 		case CKM_RSA_PKCS:
 		 	pad = RSA_PKCS1_PADDING;
 		 	break;
-		 case CKM_RSA_X_509:
-		 	pad = RSA_NO_PADDING;
-		 	break;
-		/* TODO support more then RSA */
-		 default:
+		case CKM_RSA_X_509:
+			pad = RSA_NO_PADDING;
+			break;
+		case CKM_RSA_PKCS_PSS:
+		case CKM_SHA1_RSA_PKCS_PSS:
+		case CKM_SHA224_RSA_PKCS_PSS:
+		case CKM_SHA256_RSA_PKCS_PSS:
+		case CKM_SHA384_RSA_PKCS_PSS:
+		case CKM_SHA512_RSA_PKCS_PSS:
+			pad = RSA_NO_PADDING;
+			break;
+		default:
 			EVP_PKEY_free(pkey);
-		 	return CKR_ARGUMENTS_BAD;
-		 }
+			return CKR_ARGUMENTS_BAD;
+		}
 
 		rsa = EVP_PKEY_get1_RSA(pkey);
 		EVP_PKEY_free(pkey);
@@ -473,13 +508,95 @@ CK_RV sc_pkcs11_verify_data(const unsigned char *pubkey, int pubkey_len,
 		}
 
 		rsa_outlen = RSA_public_decrypt(signat_len, signat, rsa_out, rsa, pad);
-		RSA_free(rsa);
-		if(rsa_outlen <= 0) {
+		if (rsa_outlen <= 0) {
 			free(rsa_out);
 			sc_log(context, "RSA_public_decrypt() returned %d\n", rsa_outlen);
 			return CKR_GENERAL_ERROR;
 		}
 
+		/* For PSS mechanisms we can not simply compare the "decrypted"
+		 * data -- we need to verify the PSS padding is valid
+		 */
+		if (mech->mechanism == CKM_RSA_PKCS_PSS ||
+		    mech->mechanism == CKM_SHA1_RSA_PKCS_PSS ||
+		    mech->mechanism == CKM_SHA224_RSA_PKCS_PSS ||
+		    mech->mechanism == CKM_SHA256_RSA_PKCS_PSS ||
+		    mech->mechanism == CKM_SHA384_RSA_PKCS_PSS ||
+		    mech->mechanism == CKM_SHA512_RSA_PKCS_PSS) {
+			CK_RSA_PKCS_PSS_PARAMS* param = NULL;
+			const EVP_MD *mgf_md, *pss_md;
+			unsigned char digest[EVP_MAX_MD_SIZE];
+
+			if (mech->pParameter == NULL) {
+				sc_log(context, "PSS mechanism requires parameter");
+				return CKR_MECHANISM_PARAM_INVALID;
+			}
+
+			param = (CK_RSA_PKCS_PSS_PARAMS*)mech->pParameter;
+			switch (param->mgf) {
+			case CKG_MGF1_SHA1:
+				mgf_md = EVP_sha1();
+				break;
+			case CKG_MGF1_SHA224:
+				mgf_md = EVP_sha224();
+				break;
+			case CKG_MGF1_SHA256:
+				mgf_md = EVP_sha256();
+				break;
+			case CKG_MGF1_SHA384:
+				mgf_md = EVP_sha384();
+				break;
+			case CKG_MGF1_SHA512:
+				mgf_md = EVP_sha512();
+				break;
+			default:
+				return CKR_MECHANISM_PARAM_INVALID;
+			}
+
+			switch (param->hashAlg) {
+			case CKM_SHA_1:
+				pss_md = EVP_sha1();
+				break;
+			case CKM_SHA224:
+				pss_md = EVP_sha224();
+				break;
+			case CKM_SHA256:
+				pss_md = EVP_sha256();
+				break;
+			case CKM_SHA384:
+				pss_md = EVP_sha384();
+				break;
+			case CKM_SHA512:
+				pss_md = EVP_sha512();
+				break;
+			default:
+				return CKR_MECHANISM_PARAM_INVALID;
+			}
+
+			/* for the mechanisms with hash algorithm, the data
+			 * is already added to the hash buffer, so we need
+			 * to finish the hash operation here
+			 */
+			if (mech->mechanism != CKM_RSA_PKCS_PSS) {
+				EVP_MD_CTX *md_ctx = DIGEST_CTX(md);
+				unsigned char *tmp = digest;
+				unsigned int tmp_len;
+
+				EVP_DigestFinal(md_ctx, tmp, &tmp_len);
+				data = tmp;
+				data_len = tmp_len;
+			}
+			rv = CKR_SIGNATURE_INVALID;
+			if (data_len == EVP_MD_size(pss_md) &&
+			    RSA_verify_PKCS1_PSS_mgf1(rsa, data, pss_md, mgf_md,
+			        rsa_out, EVP_MD_size(pss_md)/*sLen*/) == 1)
+				rv = CKR_OK;
+			RSA_free(rsa);
+			sc_log(context, "Returning %lu", rv);
+			return rv;
+		}
+		RSA_free(rsa);
+
 		if (rsa_outlen == data_len && memcmp(rsa_out, data, data_len) == 0)
 			rv = CKR_OK;
 		else
diff --git a/src/pkcs11/pkcs11.h b/src/pkcs11/pkcs11.h
index 61a5050df..8219b961b 100644
--- a/src/pkcs11/pkcs11.h
+++ b/src/pkcs11/pkcs11.h
@@ -480,8 +480,6 @@ struct ck_date
 
 typedef unsigned long ck_mechanism_type_t;
 
-typedef unsigned long int ck_rsa_pkcs_mgf_type_t;
-
 #define CKM_RSA_PKCS_KEY_PAIR_GEN	(0UL)
 #define CKM_RSA_PKCS			(1UL)
 #define CKM_RSA_9796			(2UL)
@@ -764,6 +762,7 @@ typedef struct CK_ECDH1_DERIVE_PARAMS {
 	unsigned char *  pPublicData;
 } CK_ECDH1_DERIVE_PARAMS;
 
+typedef unsigned long ck_rsa_pkcs_mgf_type_t;
 typedef unsigned long CK_RSA_PKCS_OAEP_SOURCE_TYPE;
 
 typedef struct CK_RSA_PKCS_OAEP_PARAMS {
diff --git a/src/pkcs11/sc-pkcs11.h b/src/pkcs11/sc-pkcs11.h
index 843245882..f0115ed04 100644
--- a/src/pkcs11/sc-pkcs11.h
+++ b/src/pkcs11/sc-pkcs11.h
@@ -119,6 +119,9 @@ struct sc_pkcs11_object_ops {
 	/* Check compatibility of PKCS#15 object usage and an asked PKCS#11 mechanism. */
 	CK_RV (*can_do)(struct sc_pkcs11_session *, void *, CK_MECHANISM_TYPE, unsigned int);
 
+	/* General validation of mechanism parameters (sign, encrypt, etc) */
+	CK_RV (*init_params)(struct sc_pkcs11_session *, CK_MECHANISM_PTR);
+
 	/* Others to be added when implemented */
 };
 
@@ -290,6 +293,10 @@ typedef struct sc_pkcs11_mechanism_type sc_pkcs11_mechanism_type_t;
 struct sc_pkcs11_operation {
 	sc_pkcs11_mechanism_type_t *type;
 	CK_MECHANISM	  mechanism;
+	union {
+		CK_RSA_PKCS_PSS_PARAMS pss;
+		CK_RSA_PKCS_OAEP_PARAMS oaep;
+	} mechanism_params;
 	struct sc_pkcs11_session *session;
 	void *		  priv_data;
 };
@@ -434,7 +441,7 @@ CK_RV sc_pkcs11_register_sign_and_hash_mechanism(struct sc_pkcs11_card *,
 #ifdef ENABLE_OPENSSL
 CK_RV sc_pkcs11_verify_data(const unsigned char *pubkey, int pubkey_len,
 	const unsigned char *pubkey_params, int pubkey_params_len,
-	CK_MECHANISM_TYPE mech, sc_pkcs11_operation_t *md,
+	CK_MECHANISM_PTR mech, sc_pkcs11_operation_t *md,
 	unsigned char *inp, int inp_len,
 	unsigned char *signat, int signat_len);
 #endif

From 2f36612d116ed1fb3ed305a5657871fa12f75011 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Sun, 22 Jul 2018 16:29:19 +0200
Subject: [PATCH 4/5] pkcs11-tool: Support for signature verification

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
---
 doc/tools/pkcs11-tool.1.xml |  14 ++
 src/tools/pkcs11-tool.c     | 273 +++++++++++++++++++++++++++---------
 2 files changed, 222 insertions(+), 65 deletions(-)

diff --git a/doc/tools/pkcs11-tool.1.xml b/doc/tools/pkcs11-tool.1.xml
index c609ec0e2..fd823c06e 100644
--- a/doc/tools/pkcs11-tool.1.xml
+++ b/doc/tools/pkcs11-tool.1.xml
@@ -481,6 +481,13 @@
 					non-zero number.</para></listitem>
 				</varlistentry>
 
+				<varlistentry>
+					<term>
+						<option>--verify</option>,
+					</term>
+					<listitem><para>Verify signature of some data.</para></listitem>
+				</varlistentry>
+
 				<varlistentry>
 					<term>
 						<option>--read-object</option>,
@@ -530,6 +537,13 @@
 					<option>--type</option> cert/privkey/pubkey).</para></listitem>
 				</varlistentry>
 
+				<varlistentry>
+					<term>
+						<option>--signature-file</option> <replaceable>filename</replaceable>
+					</term>
+					<listitem><para>The path to the signature file for signature verification</para></listitem>
+				</varlistentry>
+
 				<varlistentry>
 					<term>
 						<option>--signature-format</option> <replaceable>format</replaceable>
diff --git a/src/tools/pkcs11-tool.c b/src/tools/pkcs11-tool.c
index 64525f6ad..e3c52e2f8 100644
--- a/src/tools/pkcs11-tool.c
+++ b/src/tools/pkcs11-tool.c
@@ -150,6 +150,8 @@ enum {
 	OPT_HASH_ALGORITHM,
 	OPT_MGF,
 	OPT_SALT,
+	OPT_VERIFY,
+	OPT_SIGNATURE_FILE,
 };
 
 static const struct option options[] = {
@@ -161,6 +163,7 @@ static const struct option options[] = {
 	{ "list-objects",	0, NULL,		'O' },
 
 	{ "sign",		0, NULL,		's' },
+	{ "verify",		0, NULL,		OPT_VERIFY },
 	{ "decrypt",		0, NULL,		OPT_DECRYPT },
 	{ "hash",		0, NULL,		'h' },
 	{ "derive",		0, NULL,		OPT_DERIVE },
@@ -203,6 +206,7 @@ static const struct option options[] = {
 	{ "set-id",		1, NULL,		'e' },
 	{ "attr-from",		1, NULL,		OPT_ATTR_FROM },
 	{ "input-file",		1, NULL,		'i' },
+	{ "signature-file",	1, NULL,		OPT_SIGNATURE_FILE },
 	{ "output-file",	1, NULL,		'o' },
 	{ "signature-format",	1, NULL,		'f' },
 
@@ -230,6 +234,7 @@ static const char *option_help[] = {
 	"Show objects on token",
 
 	"Sign some data",
+	"Verify a signature of some data",
 	"Decrypt some data",
 	"Hash some data",
 	"Derive a secret key using another key and some data",
@@ -272,6 +277,7 @@ static const char *option_help[] = {
 	"Set the CKA_ID of an object, <args>= the (new) CKA_ID",
 	"Use <arg> to create some attributes when writing an object",
 	"Specify the input file",
+	"Specify the file with signature for verification",
 	"Specify the output file",
 	"Format for ECDSA signature <arg>: 'rs' (default), 'sequence', 'openssl'",
 
@@ -293,6 +299,7 @@ static const char *	app_name = "pkcs11-tool"; /* for utils.c */
 static int		verbose = 0;
 static const char *	opt_input = NULL;
 static const char *	opt_output = NULL;
+static const char *	opt_signature_file = NULL;
 static const char *	opt_module = DEFAULT_PKCS11_PROVIDER;
 static int		opt_slot_set = 0;
 static CK_SLOT_ID	opt_slot = 0;
@@ -331,8 +338,8 @@ static int		opt_derive_pass_der = 0;
 static unsigned long	opt_random_bytes = 0;
 static CK_MECHANISM_TYPE opt_hash_alg = 0;
 static unsigned long	opt_mgf = 0;
-static long	        salt_len = 0;
-static int		salt_len_given = 0; /* 0 - not given, 1 - given with input parameters */
+static long	        opt_salt_len = 0;
+static int		opt_salt_len_given = 0; /* 0 - not given, 1 - given with input parameters */
 
 static void *module = NULL;
 static CK_FUNCTION_LIST_PTR p11 = NULL;
@@ -396,6 +403,7 @@ static void		show_key(CK_SESSION_HANDLE, CK_OBJECT_HANDLE);
 static void		show_cert(CK_SESSION_HANDLE, CK_OBJECT_HANDLE);
 static void		show_dobj(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj);
 static void		sign_data(CK_SLOT_ID, CK_SESSION_HANDLE, CK_OBJECT_HANDLE);
+static void		verify_signature(CK_SLOT_ID, CK_SESSION_HANDLE, CK_OBJECT_HANDLE);
 static void		decrypt_data(CK_SLOT_ID, CK_SESSION_HANDLE, CK_OBJECT_HANDLE);
 static void		hash_data(CK_SLOT_ID, CK_SESSION_HANDLE);
 static void		derive_key(CK_SLOT_ID, CK_SESSION_HANDLE, CK_OBJECT_HANDLE);
@@ -532,6 +540,7 @@ int main(int argc, char * argv[])
 	int do_list_mechs = 0;
 	int do_list_objects = 0;
 	int do_sign = 0;
+	int do_verify = 0;
 	int do_decrypt = 0;
 	int do_hash = 0;
 	int do_derive = 0;
@@ -685,6 +694,9 @@ int main(int argc, char * argv[])
 		case 'i':
 			opt_input = optarg;
 			break;
+		case OPT_SIGNATURE_FILE:
+			opt_signature_file = optarg;
+			break;
 		case 'l':
 			need_session |= NEED_SESSION_RW;
 			opt_login = 1;
@@ -700,8 +712,8 @@ int main(int argc, char * argv[])
 			opt_mgf = p11_name_to_mgf(optarg);
 			break;
 		case OPT_SALT:
-			salt_len = (CK_ULONG) strtoul(optarg, NULL, 0);
-			salt_len_given = 1;
+			opt_salt_len = (CK_ULONG) strtoul(optarg, NULL, 0);
+			opt_salt_len_given = 1;
 			break;
 		case 'o':
 			opt_output = optarg;
@@ -726,6 +738,11 @@ int main(int argc, char * argv[])
 			do_sign = 1;
 			action_count++;
 			break;
+		case OPT_VERIFY:
+			need_session |= NEED_SESSION_RO;
+			do_verify = 1;
+			action_count++;
+			break;
 		case OPT_DECRYPT:
 			need_session |= NEED_SESSION_RW;
 			do_decrypt = 1;
@@ -1037,6 +1054,16 @@ int main(int argc, char * argv[])
 			util_fatal("Private key not found");
 	}
 
+	if (do_verify) {
+		if (!find_object(session, CKO_PUBLIC_KEY, &object,
+		        opt_object_id_len ? opt_object_id : NULL,
+		        opt_object_id_len, 0) &&
+		    !find_object(session, CKO_CERTIFICATE, &object,
+		        opt_object_id_len ? opt_object_id : NULL,
+		        opt_object_id_len, 0))
+			util_fatal("Public key nor certificate not found");
+	}
+
 	/* before list objects, so we can see a derived key */
 	if (do_derive)
 		derive_key(opt_slot, session, object);
@@ -1047,6 +1074,9 @@ int main(int argc, char * argv[])
 	if (do_sign)
 		sign_data(opt_slot, session, object);
 
+	if (do_verify)
+		verify_signature(opt_slot, session, object);
+
 	if (do_decrypt)
 		decrypt_data(opt_slot, session, object);
 
@@ -1636,7 +1666,7 @@ static int unlock_pin(CK_SLOT_ID slot, CK_SESSION_HANDLE sess, int login_type)
 }
 
 /* return digest length in bytes */
-static unsigned long figure_pss_salt_length(const int hash) {
+static unsigned long hash_length(const int hash) {
 	unsigned long sLen = 0;
 	switch (hash) {
 	case  CKM_SHA_1:
@@ -1662,26 +1692,16 @@ static unsigned long figure_pss_salt_length(const int hash) {
 	return sLen;
 }
 
-static void sign_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session,
-		CK_OBJECT_HANDLE key)
+static unsigned long
+parse_pss_params(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key,
+    CK_MECHANISM *mech, CK_RSA_PKCS_PSS_PARAMS *pss_params)
 {
-	unsigned char	in_buffer[1025], sig_buffer[512];
-	CK_MECHANISM	mech;
-	CK_RSA_PKCS_PSS_PARAMS pss_params;
-	CK_RV		rv;
-	CK_ULONG	sig_len;
-	int		fd, r;
+	unsigned long hashlen = 0;
 
-	unsigned long hashlen = 0, modlen = 0;
-
-	if (!opt_mechanism_used)
-		if (!find_mechanism(slot, CKF_SIGN|CKF_HW, NULL, 0, &opt_mechanism))
-			util_fatal("Sign mechanism not supported");
+	if (pss_params == NULL)
+		return 0;
 
-	fprintf(stderr, "Using signature algorithm %s\n", p11_mechanism_to_name(opt_mechanism));
-	memset(&mech, 0, sizeof(mech));
-	mech.mechanism = opt_mechanism;
-	pss_params.hashAlg = 0;
+	pss_params->hashAlg = 0;
 
 	if (opt_hash_alg != 0 && opt_mechanism != CKM_RSA_PKCS_PSS)
 		util_fatal("The hash-algorithm is applicable only to "
@@ -1690,93 +1710,118 @@ static void sign_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session,
 	/* set "default" MGF and hash algorithms. We can overwrite MGF later */
 	switch (opt_mechanism) {
 	case CKM_RSA_PKCS_PSS:
-		pss_params.hashAlg = opt_hash_alg;
+		pss_params->hashAlg = opt_hash_alg;
 
 		switch (opt_hash_alg) {
 		case CKM_SHA224:
-			pss_params.mgf = CKG_MGF1_SHA224;
+			pss_params->mgf = CKG_MGF1_SHA224;
 			break;
 		case CKM_SHA256:
-			pss_params.mgf = CKG_MGF1_SHA256;
+			pss_params->mgf = CKG_MGF1_SHA256;
 			break;
 		case CKM_SHA384:
-			pss_params.mgf = CKG_MGF1_SHA384;
+			pss_params->mgf = CKG_MGF1_SHA384;
 			break;
 		case CKM_SHA512:
-			pss_params.mgf = CKG_MGF1_SHA512;
+			pss_params->mgf = CKG_MGF1_SHA512;
 			break;
 		default:
 			/* the PSS should use SHA-1 if not specified */
-			pss_params.hashAlg = CKM_SHA_1;
+			pss_params->hashAlg = CKM_SHA_1;
 			/* fallthrough */
 		case CKM_SHA_1:
-			pss_params.mgf = CKG_MGF1_SHA1;
+			pss_params->mgf = CKG_MGF1_SHA1;
 		}
 		break;
 
 	case CKM_SHA1_RSA_PKCS_PSS:
-		pss_params.hashAlg = CKM_SHA_1;
-		pss_params.mgf = CKG_MGF1_SHA1;
+		pss_params->hashAlg = CKM_SHA_1;
+		pss_params->mgf = CKG_MGF1_SHA1;
 		break;
 
 	case CKM_SHA224_RSA_PKCS_PSS:
-		pss_params.hashAlg = CKM_SHA224;
-		pss_params.mgf = CKG_MGF1_SHA224;
+		pss_params->hashAlg = CKM_SHA224;
+		pss_params->mgf = CKG_MGF1_SHA224;
 		break;
 
 	case CKM_SHA256_RSA_PKCS_PSS:
-		pss_params.hashAlg = CKM_SHA256;
-		pss_params.mgf = CKG_MGF1_SHA256;
+		pss_params->hashAlg = CKM_SHA256;
+		pss_params->mgf = CKG_MGF1_SHA256;
 		break;
 
 	case CKM_SHA384_RSA_PKCS_PSS:
-		pss_params.hashAlg = CKM_SHA384;
-		pss_params.mgf = CKG_MGF1_SHA384;
+		pss_params->hashAlg = CKM_SHA384;
+		pss_params->mgf = CKG_MGF1_SHA384;
 		break;
 
 	case CKM_SHA512_RSA_PKCS_PSS:
-		pss_params.hashAlg = CKM_SHA512;
-		pss_params.mgf = CKG_MGF1_SHA512;
+		pss_params->hashAlg = CKM_SHA512;
+		pss_params->mgf = CKG_MGF1_SHA512;
 		break;
 	}
 
 	/* One of RSA-PSS mechanisms above: They need parameters */
-	if (pss_params.hashAlg) {
+	if (pss_params->hashAlg) {
 		if (opt_mgf != 0)
-			pss_params.mgf = opt_mgf;
+			pss_params->mgf = opt_mgf;
 
-		hashlen = figure_pss_salt_length(pss_params.hashAlg);
+		hashlen = hash_length(pss_params->hashAlg);
 
-		if (salt_len_given == 1) { /* salt size explicitly given */
-			if (salt_len < 0 && salt_len != -1 && salt_len != -2)
-				util_fatal("Salt length must be greater or equal \
-to zero, or equal to -1 (meaning: use digest size) or to -2 \
-(meaning: use maximum permissible size");
+		if (opt_salt_len_given == 1) { /* salt size explicitly given */
+			unsigned long modlen = 0;
+			if (opt_salt_len < 0 && opt_salt_len != -1 && opt_salt_len != -2)
+				util_fatal("Salt length must be greater or equal "
+				    "to zero, or equal to -1 (meaning: use digest size) "
+				    "or to -2 (meaning: use maximum permissible size");
 		  
 			modlen = (get_private_key_length(session, key) + 7) / 8;
-			switch(salt_len) {
+			switch (opt_salt_len) {
 			case -1: /* salt size equals to digest size */
-				pss_params.sLen = hashlen;
+				pss_params->sLen = hashlen;
 				break;
 			case -2: /* maximum permissible salt len */
-				pss_params.sLen = modlen - hashlen -2;
+				pss_params->sLen = modlen - hashlen -2;
 				break;
 			default: /* use given size but its value must be >= 0 */
-				pss_params.sLen = salt_len;
+				pss_params->sLen = opt_salt_len;
 				break;
-			} /* end switch (salt_len_given) */
+			} /* end switch (opt_salt_len_given) */
 		} else { /* use default: salt len of digest size */
-			pss_params.sLen = hashlen;
+			pss_params->sLen = hashlen;
 		}
 
-		mech.pParameter = &pss_params;
-		mech.ulParameterLen = sizeof(pss_params);
+		mech->pParameter = pss_params;
+		mech->ulParameterLen = sizeof(*pss_params);
 
 		fprintf(stderr, "PSS parameters: hashAlg=%s, mgf=%s, salt_len=%lu B\n",
-			p11_mechanism_to_name(pss_params.hashAlg),
-			p11_mgf_to_name(pss_params.mgf),
-			pss_params.sLen);
+			p11_mechanism_to_name(pss_params->hashAlg),
+			p11_mgf_to_name(pss_params->mgf),
+			pss_params->sLen);
 	}
+	return hashlen;
+}
+
+static void sign_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session,
+		CK_OBJECT_HANDLE key)
+{
+	unsigned char	in_buffer[1025], sig_buffer[512];
+	CK_MECHANISM	mech;
+	CK_RSA_PKCS_PSS_PARAMS pss_params;
+	CK_RV		rv;
+	CK_ULONG	sig_len;
+	int		fd, r;
+	unsigned long	hashlen;
+
+	if (!opt_mechanism_used)
+		if (!find_mechanism(slot, CKF_SIGN|CKF_HW, NULL, 0, &opt_mechanism))
+			util_fatal("Sign mechanism not supported");
+
+	fprintf(stderr, "Using signature algorithm %s\n", p11_mechanism_to_name(opt_mechanism));
+	memset(&mech, 0, sizeof(mech));
+	mech.mechanism = opt_mechanism;
+	hashlen = parse_pss_params(session, key, &mech, &pss_params);
+	if (hashlen == 0)
+		util_fatal("Invalid RSA-PSS parameters");
 
 	if (opt_input == NULL)
 		fd = 0;
@@ -1787,12 +1832,15 @@ to zero, or equal to -1 (meaning: use digest size) or to -2 \
 	if (r < 0)
 		util_fatal("Cannot read from %s: %m", opt_input);
 
-	if (opt_mechanism == CKM_RSA_PKCS_PSS && (unsigned long)r != hashlen)
-		util_fatal("For %s mechanism, message size (got %d bytes) must be equal to specified digest length (%lu)\n",
-			p11_mechanism_to_name(opt_mechanism), r, hashlen);
+	if (opt_mechanism == CKM_RSA_PKCS_PSS) {
+		if  ((unsigned long)r != hashlen)
+			util_fatal("For %s mechanism, message size (got %d bytes) "
+			     "must be equal to specified digest length (%lu)\n",
+			    p11_mechanism_to_name(opt_mechanism), r, hashlen);
+	}
 
 	rv = CKR_CANCEL;
-	if (r < (int) sizeof(in_buffer))   {
+	if (r < (int) sizeof(in_buffer)) {
 		rv = p11->C_SignInit(session, &mech, key);
 		if (rv != CKR_OK)
 			p11_fatal("C_SignInit", rv);
@@ -1833,12 +1881,16 @@ to zero, or equal to -1 (meaning: use digest size) or to -2 \
 		util_fatal("failed to open %s: %m", opt_output);
 	}
 
-	if (opt_mechanism == CKM_ECDSA || opt_mechanism == CKM_ECDSA_SHA1 || opt_mechanism == CKM_ECDSA_SHA256 || opt_mechanism == CKM_ECDSA_SHA384 || opt_mechanism == CKM_ECDSA_SHA512 || opt_mechanism == CKM_ECDSA_SHA224) {
-		if (opt_sig_format &&  (!strcmp(opt_sig_format, "openssl") || !strcmp(opt_sig_format, "sequence"))) {
+	if (opt_mechanism == CKM_ECDSA || opt_mechanism == CKM_ECDSA_SHA1 ||
+	    opt_mechanism == CKM_ECDSA_SHA256 || opt_mechanism == CKM_ECDSA_SHA384 ||
+	    opt_mechanism == CKM_ECDSA_SHA512 || opt_mechanism == CKM_ECDSA_SHA224) {
+		if (opt_sig_format && (!strcmp(opt_sig_format, "openssl") ||
+		                       !strcmp(opt_sig_format, "sequence"))) {
 			unsigned char *seq;
 			size_t seqlen;
 
-			if (sc_asn1_sig_value_rs_to_sequence(NULL, sig_buffer, sig_len, &seq, &seqlen)) {
+			if (sc_asn1_sig_value_rs_to_sequence(NULL, sig_buffer,
+			    sig_len, &seq, &seqlen)) {
 				util_fatal("Failed to convert signature to ASN.1 sequence format");
 			}
 
@@ -1856,6 +1908,97 @@ to zero, or equal to -1 (meaning: use digest size) or to -2 \
 		close(fd);
 }
 
+static void verify_signature(CK_SLOT_ID slot, CK_SESSION_HANDLE session,
+		CK_OBJECT_HANDLE key)
+{
+	unsigned char	in_buffer[1025], sig_buffer[512];
+	CK_MECHANISM	mech;
+	CK_RSA_PKCS_PSS_PARAMS pss_params;
+	CK_RV		rv;
+	CK_ULONG	sig_len;
+	int		fd, fd2, r, r2;
+	unsigned long   hashlen;
+
+	if (!opt_mechanism_used)
+		if (!find_mechanism(slot, CKF_VERIFY|CKF_HW, NULL, 0, &opt_mechanism))
+			util_fatal("Mechanism not supported for signature verification");
+
+	fprintf(stderr, "Using signature algorithm %s\n", p11_mechanism_to_name(opt_mechanism));
+	memset(&mech, 0, sizeof(mech));
+	mech.mechanism = opt_mechanism;
+	hashlen = parse_pss_params(session, key, &mech, &pss_params);
+	if (hashlen == 0)
+		util_fatal("Invalid RSA-PSS parameters");
+
+	/* Open a signature file */
+	if (opt_signature_file == NULL)
+		util_fatal("No file with signature provided. Use --signature-file");
+	else if ((fd2 = open(opt_signature_file, O_RDONLY|O_BINARY)) < 0)
+		util_fatal("Cannot open %s: %m", opt_signature_file);
+
+	r2 = read(fd2, sig_buffer, sizeof(sig_buffer));
+	if (r2 < 0)
+		util_fatal("Cannot read from %s: %m", opt_signature_file);
+
+	close(fd2);
+
+	/* Open the data file */
+	if (opt_input == NULL)
+		fd = 0;
+	else if ((fd = open(opt_input, O_RDONLY|O_BINARY)) < 0)
+		util_fatal("Cannot open %s: %m", opt_input);
+
+	r = read(fd, in_buffer, sizeof(in_buffer));
+	if (r < 0)
+		util_fatal("Cannot read from %s: %m", opt_input);
+
+	if (opt_mechanism == CKM_RSA_PKCS_PSS) {
+		if ((unsigned long)r != hashlen)
+			util_fatal("For %s mechanism, message size (got %d bytes)"
+			    " must be equal to specified digest length (%lu)\n",
+			    p11_mechanism_to_name(opt_mechanism), r, hashlen);
+	}
+
+	rv = CKR_CANCEL;
+	if (r < (int) sizeof(in_buffer)) {
+		rv = p11->C_VerifyInit(session, &mech, key);
+		if (rv != CKR_OK)
+			p11_fatal("C_VerifyInit", rv);
+
+		sig_len = r2;
+		rv =  p11->C_Verify(session, in_buffer, r, sig_buffer, sig_len);
+	}
+
+	if (rv != CKR_OK) {
+		rv = p11->C_VerifyInit(session, &mech, key);
+		if (rv != CKR_OK)
+			p11_fatal("C_VerifyInit", rv);
+
+		do   {
+			rv = p11->C_VerifyUpdate(session, in_buffer, r);
+			if (rv != CKR_OK)
+				p11_fatal("C_VerifyUpdate", rv);
+
+			r = read(fd, in_buffer, sizeof(in_buffer));
+		} while (r > 0);
+
+		sig_len = sizeof(sig_buffer);
+		rv = p11->C_VerifyFinal(session, sig_buffer, sig_len);
+		if (rv != CKR_OK)
+			p11_fatal("C_VerifyFinal", rv);
+	}
+
+	if (fd != 0)
+		close(fd);
+
+	if (rv == CKR_OK)
+		printf("Signature is valid\n");
+	else if (rv == CKR_SIGNATURE_INVALID)
+		printf("Invalid signature\n");
+	else
+		printf("Cryptoki returned erorr: %s\n", CKR2Str(rv));
+}
+
 
 static void decrypt_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session,
 		CK_OBJECT_HANDLE key)

From 256502bed97d56a6813c0b4a7d4c64ee1ff0606e Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Fri, 14 Sep 2018 17:27:11 +0200
Subject: [PATCH 5/5] slot: Switch cleanup steps to avoid segfaults on errors

and some more sanity checking

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
---
 src/pkcs11/framework-pkcs15.c | 2 +-
 src/pkcs11/slot.c             | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/pkcs11/framework-pkcs15.c b/src/pkcs11/framework-pkcs15.c
index a75d239f4..85e12df66 100644
--- a/src/pkcs11/framework-pkcs15.c
+++ b/src/pkcs11/framework-pkcs15.c
@@ -365,7 +365,7 @@ pkcs15_unbind(struct sc_pkcs11_card *p11card)
 
 		unlock_card(fw_data);
 
-		if (fw_data->p15_card) {
+		if (fw_data->p15_card && fw_data->p15_card->card) {
 			if (idx == 0) {
 				int rc = sc_detect_card_presence(fw_data->p15_card->card->reader);
 				if (rc <= 0 || rc & SC_READER_CARD_CHANGED) {
diff --git a/src/pkcs11/slot.c b/src/pkcs11/slot.c
index fe322e68e..3102bf986 100644
--- a/src/pkcs11/slot.c
+++ b/src/pkcs11/slot.c
@@ -374,10 +374,10 @@ CK_RV card_detect(sc_reader_t *reader)
 
 fail:
 	if (free_p11card) {
-		if (p11card->card != NULL)
-			sc_disconnect_card(p11card->card);
 		if (p11card->framework)
 			p11card->framework->unbind(p11card);
+		if (p11card->card != NULL)
+			sc_disconnect_card(p11card->card);
 		free(p11card);
 	}
 
From 2fd8e278f5d3664555cad706d7270229c87cae56 Mon Sep 17 00:00:00 2001
From: Doug Engert <deengert@gmail.com>
Date: Wed, 17 Oct 2018 16:07:20 -0500
Subject: [PATCH] pkcs11/openssl.c - add missing mechanisms fixes #1497

 On branch pkcs11-openssl-c
 Changes to be committed:
	modified:   ../pkcs11/openssl.c
---
 src/pkcs11/openssl.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/pkcs11/openssl.c b/src/pkcs11/openssl.c
index 00b9814e4..fb9f8fea8 100644
--- a/src/pkcs11/openssl.c
+++ b/src/pkcs11/openssl.c
@@ -449,6 +449,8 @@ CK_RV sc_pkcs11_verify_data(const unsigned char *pubkey, int pubkey_len,
 		return CKR_GENERAL_ERROR;
 
 	if (md != NULL && (mech->mechanism == CKM_SHA1_RSA_PKCS
+		|| mech->mechanism == CKM_MD5_RSA_PKCS
+		|| mech->mechanism == CKM_RIPEMD160_RSA_PKCS
 		|| mech->mechanism == CKM_SHA224_RSA_PKCS
 		|| mech->mechanism == CKM_SHA256_RSA_PKCS
 		|| mech->mechanism == CKM_SHA384_RSA_PKCS
@@ -478,6 +480,8 @@ CK_RV sc_pkcs11_verify_data(const unsigned char *pubkey, int pubkey_len,
 		sc_log(context, "Trying to verify using low-level API");
 		switch (mech->mechanism) {
 		case CKM_RSA_PKCS:
+		case CKM_MD5_RSA_PKCS:
+		case CKM_RIPEMD160_RSA_PKCS:
 		 	pad = RSA_PKCS1_PADDING;
 		 	break;
 		case CKM_RSA_X_509:

