The attached patch against openssl-SNAP-20090607 adds support for X.509
certificates that contain an RSASSA-PSS signature (see  PKCS1 #2.1 and
RFC 4055).

At the moment, only verification is supported, PSS parameters must be
the default ones (either omitted or coded explicitly).

The main idea of the patch is to use a new EVP_MD for "sha1 coupled to
PSS", see my mail from 1st of June (20090601112625.ga3...@kaiser.cx).

Thanks for your feedback and/or for merging this patch.

diff --git a/crypto/asn1/a_verify.c b/crypto/asn1/a_verify.c
index cecdb13..3c57103 100644
--- a/crypto/asn1/a_verify.c
+++ b/crypto/asn1/a_verify.c
@@ -129,14 +129,15 @@ err:
 int ASN1_item_verify(const ASN1_ITEM *it, X509_ALGOR *a, ASN1_BIT_STRING *signature,
 	     void *asn, EVP_PKEY *pkey)
 	{
-	EVP_MD_CTX ctx;
-	const EVP_MD *type = NULL;
+	EVP_MD_CTX mctx;
+	const EVP_MD *md = NULL;
 	unsigned char *buf_in=NULL;
 	int ret= -1,inl;
+	EVP_PKEY_CTX *pctx;
 
 	int mdnid, pknid;
 
-	EVP_MD_CTX_init(&ctx);
+	EVP_MD_CTX_init(&mctx);
 
 	/* Convert signature OID into digest and public key OIDs */
 	if (!OBJ_find_sigid_algs(OBJ_obj2nid(a->algorithm), &mdnid, &pknid))
@@ -144,8 +145,8 @@ int ASN1_item_verify(const ASN1_ITEM *it, X509_ALGOR *a, ASN1_BIT_STRING *signat
 		ASN1err(ASN1_F_ASN1_ITEM_VERIFY,ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM);
 		goto err;
 		}
-	type=EVP_get_digestbynid(mdnid);
-	if (type == NULL)
+	md=EVP_get_digestbynid(mdnid);
+	if (md == NULL)
 		{
 		ASN1err(ASN1_F_ASN1_ITEM_VERIFY,ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM);
 		goto err;
@@ -158,13 +159,22 @@ int ASN1_item_verify(const ASN1_ITEM *it, X509_ALGOR *a, ASN1_BIT_STRING *signat
 		goto err;
 		}
 
-	if (!EVP_VerifyInit_ex(&ctx,type, NULL))
+	if (!EVP_DigestVerifyInit(&mctx, &pctx, md, NULL, pkey))
 		{
 		ASN1err(ASN1_F_ASN1_ITEM_VERIFY,ERR_R_EVP_LIB);
 		ret=0;
 		goto err;
 		}
 
+	if (md && md->md_ctrl)
+		{
+		if (!md->md_ctrl(&mctx, EVP_MD_CTRL_SET_PCTX_PARAMS, 0, (void *)a))
+			{
+			ASN1err(ASN1_F_ASN1_ITEM_VERIFY,ASN1_R_ERROR_SETTING_CIPHER_PARAMS);
+			goto err;
+			}
+		}
+
 	inl = ASN1_item_i2d(asn, &buf_in, it);
 	
 	if (buf_in == NULL)
@@ -173,24 +183,24 @@ int ASN1_item_verify(const ASN1_ITEM *it, X509_ALGOR *a, ASN1_BIT_STRING *signat
 		goto err;
 		}
 
-	EVP_VerifyUpdate(&ctx,(unsigned char *)buf_in,inl);
+	EVP_DigestVerifyUpdate(&mctx,(unsigned char *)buf_in,inl);
 
 	OPENSSL_cleanse(buf_in,(unsigned int)inl);
 	OPENSSL_free(buf_in);
 
-	if (EVP_VerifyFinal(&ctx,(unsigned char *)signature->data,
-			(unsigned int)signature->length,pkey) <= 0)
+	if (EVP_DigestVerifyFinal(&mctx,(unsigned char *)signature->data,
+			(unsigned int)signature->length) <= 0)
 		{
 		ASN1err(ASN1_F_ASN1_ITEM_VERIFY,ERR_R_EVP_LIB);
 		ret=0;
 		goto err;
 		}
-	/* we don't need to zero the 'ctx' because we just checked
+	/* we don't need to zero the 'mctx' because we just checked
 	 * public information */
-	/* memset(&ctx,0,sizeof(ctx)); */
+	/* memset(&mctx,0,sizeof(mctx)); */
 	ret=1;
 err:
-	EVP_MD_CTX_cleanup(&ctx);
+	EVP_MD_CTX_cleanup(&mctx);
 	return(ret);
 	}
 
diff --git a/crypto/evp/c_alld.c b/crypto/evp/c_alld.c
index 5032e7c..cee322d 100644
--- a/crypto/evp/c_alld.c
+++ b/crypto/evp/c_alld.c
@@ -85,6 +85,7 @@ void OpenSSL_add_all_digests(void)
 	EVP_add_digest(EVP_sha1());
 	EVP_add_digest_alias(SN_sha1,"ssl3-sha1");
 	EVP_add_digest_alias(SN_sha1WithRSAEncryption,SN_sha1WithRSA);
+	EVP_add_digest(EVP_sha1Pss());
 #ifndef OPENSSL_NO_DSA
 	EVP_add_digest(EVP_dss1());
 	EVP_add_digest_alias(SN_dsaWithSHA1,SN_dsaWithSHA1_2);
diff --git a/crypto/evp/evp.h b/crypto/evp/evp.h
index 8c77419..ba761ab 100644
--- a/crypto/evp/evp.h
+++ b/crypto/evp/evp.h
@@ -224,6 +224,7 @@ typedef int evp_verify_method(int type,const unsigned char *m,
 /* Minimum Algorithm specific ctrl value */
 
 #define	EVP_MD_CTRL_ALG_CTRL			0x1000
+#define	EVP_MD_CTRL_SET_PCTX_PARAMS  (EVP_MD_CTRL_ALG_CTRL + 0x10)
 
 #define EVP_PKEY_NULL_method	NULL,NULL,{0,0,0,0}
 
@@ -620,6 +621,7 @@ const EVP_MD *EVP_md5(void);
 #ifndef OPENSSL_NO_SHA
 const EVP_MD *EVP_sha(void);
 const EVP_MD *EVP_sha1(void);
+const EVP_MD *EVP_sha1Pss(void);
 const EVP_MD *EVP_dss(void);
 const EVP_MD *EVP_dss1(void);
 const EVP_MD *EVP_ecdsa(void);
diff --git a/crypto/evp/m_sha1.c b/crypto/evp/m_sha1.c
index 9a2790f..ccb96b4 100644
--- a/crypto/evp/m_sha1.c
+++ b/crypto/evp/m_sha1.c
@@ -97,6 +97,76 @@ const EVP_MD *EVP_sha1(void)
 	{
 	return(&sha1_md);
 	}
+
+static int sha1Pss_ctrl(EVP_MD_CTX *ctx, int cmd, int p1, void *p2)
+	{
+	int ptype = V_ASN1_UNDEF;
+	void *_pval;
+	ASN1_OBJECT *algor = NULL;
+	ASN1_STRING *pval = NULL;
+	const unsigned char  *p;
+	PSS_PARAMS *pss = NULL;
+
+	if (cmd == EVP_MD_CTRL_SET_PCTX_PARAMS)
+		{
+		/* p2 == signature algorithm, including parameters */
+		if ((!ctx->pctx) || (!p2))
+			return 0;
+
+		X509_ALGOR_get0(&algor, &ptype, &_pval, (X509_ALGOR *)p2);
+		pval = _pval;
+
+		if ((!algor) || (!ctx->digest) ||
+			 (OBJ_obj2nid(algor) != ctx->digest->pkey_type))
+			return 0;
+
+		if ((ptype != V_ASN1_SEQUENCE) || (!pval))
+			return 0;
+
+		p = pval->data;
+		pss = d2i_PSS_PARAMS(NULL, &p, pval->length);
+		if (!pss)
+			return 0;
+
+		if (!PSS_PARAMS_is_default(pss))
+			{
+			PSS_PARAMS_free(pss);
+			/* unsupported paramaters */
+			return 0;
+			}
+
+		EVP_PKEY_CTX_set_rsa_padding(ctx->pctx, RSA_PKCS1_PSS_PADDING);
+
+		PSS_PARAMS_free(pss);
+		return 1;
+		} 
+
+	/* invalid command */
+	return 0;
+	}
+
+
+static const EVP_MD sha1Pss_md=
+	{
+	NID_sha1Pss,
+	NID_rsaSsaPss,
+	SHA_DIGEST_LENGTH,
+	EVP_MD_FLAG_PKEY_DIGEST,
+	init,
+	update,
+	final,
+	NULL,
+	NULL,
+	EVP_PKEY_RSA_method,
+	SHA_CBLOCK,
+	sizeof(EVP_MD *)+sizeof(SHA_CTX),
+	sha1Pss_ctrl
+	};
+
+const EVP_MD *EVP_sha1Pss(void)
+	{
+	return(&sha1Pss_md);
+	}
 #endif
 
 #ifndef OPENSSL_NO_SHA256
diff --git a/crypto/objects/obj_xref.txt b/crypto/objects/obj_xref.txt
index e45b3d3..8876e4f 100644
--- a/crypto/objects/obj_xref.txt
+++ b/crypto/objects/obj_xref.txt
@@ -6,6 +6,7 @@ md2WithRSAEncryption	md2	rsaEncryption
 md5WithRSAEncryption	md5	rsaEncryption
 shaWithRSAEncryption	sha	rsaEncryption
 sha1WithRSAEncryption	sha1	rsaEncryption
+rsaSsaPss				sha1Pss	rsaEncryption
 md4WithRSAEncryption	md4	rsaEncryption
 sha256WithRSAEncryption sha256	rsaEncryption
 sha384WithRSAEncryption	sha384	rsaEncryption
diff --git a/crypto/objects/objects.txt b/crypto/objects/objects.txt
index 52ac0a6..45ddb59 100644
--- a/crypto/objects/objects.txt
+++ b/crypto/objects/objects.txt
@@ -166,6 +166,8 @@ pkcs1 3			: RSA-MD4		: md4WithRSAEncryption
 pkcs1 4			: RSA-MD5		: md5WithRSAEncryption
 pkcs1 5			: RSA-SHA1		: sha1WithRSAEncryption
 # According to PKCS #1 version 2.1
+pkcs1 8		: MGF1				: mgf1
+pkcs1 10		: RSASSA-PSS		: rsaSsaPss
 pkcs1 11		: RSA-SHA256		: sha256WithRSAEncryption
 pkcs1 12		: RSA-SHA384		: sha384WithRSAEncryption
 pkcs1 13		: RSA-SHA512		: sha512WithRSAEncryption
@@ -645,6 +647,7 @@ algorithm 17		: DES-EDE		: des-ede
 			: DESX-CBC		: desx-cbc
 algorithm 18		: SHA			: sha
 algorithm 26		: SHA1			: sha1
+						: SHA1PSS		: sha1Pss
 !Cname dsaWithSHA1-2
 algorithm 27		: DSA-SHA1-old		: dsaWithSHA1-old
 algorithm 29		: RSA-SHA1-2		: sha1WithRSA
diff --git a/crypto/ossl_typ.h b/crypto/ossl_typ.h
index 12bd701..2a27557 100644
--- a/crypto/ossl_typ.h
+++ b/crypto/ossl_typ.h
@@ -138,6 +138,8 @@ typedef struct dsa_method DSA_METHOD;
 typedef struct rsa_st RSA;
 typedef struct rsa_meth_st RSA_METHOD;
 
+typedef struct pss_params_st PSS_PARAMS;
+
 typedef struct rand_meth_st RAND_METHOD;
 
 typedef struct ecdh_method ECDH_METHOD;
diff --git a/crypto/rsa/rsa.h b/crypto/rsa/rsa.h
index cf74343..1b8064d 100644
--- a/crypto/rsa/rsa.h
+++ b/crypto/rsa/rsa.h
@@ -388,6 +388,19 @@ void *RSA_get_ex_data(const RSA *r, int idx);
 RSA *RSAPublicKey_dup(RSA *rsa);
 RSA *RSAPrivateKey_dup(RSA *rsa);
 
+
+struct pss_params_st
+	{
+	X509_ALGOR *hash;
+	X509_ALGOR *mgf;
+	ASN1_INTEGER *salt_len;
+	ASN1_INTEGER *trailer;
+	};
+
+DECLARE_ASN1_FUNCTIONS(PSS_PARAMS)
+int PSS_PARAMS_is_default(const PSS_PARAMS *p);
+
+
 /* BEGIN ERROR CODES */
 /* The following lines are auto generated by the script mkerr.pl. Any changes
  * made after this point may be overwritten when the script is next run.
diff --git a/crypto/rsa/rsa_asn1.c b/crypto/rsa/rsa_asn1.c
index 4efca8c..d8cf307 100644
--- a/crypto/rsa/rsa_asn1.c
+++ b/crypto/rsa/rsa_asn1.c
@@ -61,6 +61,7 @@
 #include <openssl/bn.h>
 #include <openssl/rsa.h>
 #include <openssl/asn1t.h>
+#include <openssl/x509.h>
 
 /* Override the default free and new methods */
 static int rsa_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
@@ -109,3 +110,37 @@ RSA *RSAPrivateKey_dup(RSA *rsa)
 	{
 	return ASN1_item_dup(ASN1_ITEM_rptr(RSAPrivateKey), rsa);
 	}
+
+
+
+ASN1_SEQUENCE(PSS_PARAMS) = {
+	ASN1_OPT(PSS_PARAMS, hash, X509_ALGOR),
+	ASN1_OPT(PSS_PARAMS, mgf, X509_ALGOR),
+	ASN1_OPT(PSS_PARAMS, salt_len, ASN1_INTEGER),
+	ASN1_OPT(PSS_PARAMS, trailer, ASN1_INTEGER)
+} ASN1_SEQUENCE_END(PSS_PARAMS)
+
+IMPLEMENT_ASN1_FUNCTIONS(PSS_PARAMS)
+
+/* check if the given parameter set equals RSASSA-PSS' default parameters
+   this is the case when the parameter set is empty or when the default
+   values are coded explicitly */
+int PSS_PARAMS_is_default(const PSS_PARAMS *p)
+	{
+	if (!p->hash && !p->mgf && !p->salt_len && !p->trailer)
+		return 1;
+
+	if (!p->hash || !(OBJ_obj2nid(p->hash->algorithm) == NID_sha1))
+		return 0;
+
+	if (!p->mgf || !(OBJ_obj2nid(p->mgf->algorithm) == NID_mgf1))
+		return 0;
+	/* in addition, we could check here that MGF1 uses sha1 */
+
+	if (!p->salt_len || !(ASN1_INTEGER_get(p->salt_len) == 20))
+		return 0;
+	if (!p->trailer || !(ASN1_INTEGER_get(p->trailer) == 1))
+		return 0;
+
+	return 1;
+	}

Reply via email to