simo / rpms / softhsm

Forked from rpms/softhsm 5 years ago
Clone
Blob Blame History Raw
From e12d04b256f1c856003ae88c5c3bfe07ce332fa7 Mon Sep 17 00:00:00 2001
From: Nikos Mavrogiannopoulos <nmav@redhat.com>
Date: Mon, 24 Jul 2017 13:52:59 +0200
Subject: [PATCH] Added support for CKM_RSA_PKCS_PSS

This mechanism is the most likely to be used by higher level
software utilizing RSA-PSS as it is in par with CKM_RSA_PKCS
and does not require to provide the whole data to be hashed.
---
 src/lib/SoftHSM.cpp                  | 112 +++++++++++++++++++++
 src/lib/crypto/AsymmetricAlgorithm.h |   1 +
 src/lib/crypto/OSSLRSA.cpp           | 190 +++++++++++++++++++++++++++++++++++
 src/lib/test/SignVerifyTests.cpp     |  47 +++++++++
 src/lib/test/SignVerifyTests.h       |   1 +
 5 files changed, 351 insertions(+)

diff --git a/src/lib/SoftHSM.cpp b/src/lib/SoftHSM.cpp
index ee94d3f7..f2a8f594 100644
--- a/src/lib/SoftHSM.cpp
+++ b/src/lib/SoftHSM.cpp
@@ -640,6 +640,10 @@ CK_RV SoftHSM::C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMech
 #ifdef HAVE_AES_KEY_WRAP_PAD
 	nrSupportedMechanisms += 1;
 #endif
+#ifdef WITH_OPENSSL
+	nrSupportedMechanisms += 1; // CKM_RSA_PKCS_PSS
+#endif
+
 	CK_MECHANISM_TYPE supportedMechanisms[] =
 	{
 #ifndef WITH_FIPS
@@ -670,6 +674,9 @@ CK_RV SoftHSM::C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMech
 		CKM_SHA256_RSA_PKCS,
 		CKM_SHA384_RSA_PKCS,
 		CKM_SHA512_RSA_PKCS,
+#ifdef WITH_OPENSSL
+		CKM_RSA_PKCS_PSS,
+#endif
 		CKM_SHA1_RSA_PKCS_PSS,
 		CKM_SHA224_RSA_PKCS_PSS,
 		CKM_SHA256_RSA_PKCS_PSS,
@@ -924,6 +931,9 @@ CK_RV SoftHSM::C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_
 		case CKM_SHA256_RSA_PKCS:
 		case CKM_SHA384_RSA_PKCS:
 		case CKM_SHA512_RSA_PKCS:
+#ifdef WITH_OPENSSL
+		case CKM_RSA_PKCS_PSS:
+#endif
 		case CKM_SHA1_RSA_PKCS_PSS:
 		case CKM_SHA224_RSA_PKCS_PSS:
 		case CKM_SHA256_RSA_PKCS_PSS:
@@ -3749,6 +3759,58 @@ CK_RV SoftHSM::AsymSignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechan
 			bAllowMultiPartOp = true;
 			isRSA = true;
 			break;
+		case CKM_RSA_PKCS_PSS:
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS))
+			{
+				ERROR_MSG("Invalid RSA-PSS parameters");
+				return CKR_ARGUMENTS_BAD;
+			}
+			mechanism = AsymMech::RSA_PKCS_PSS;
+			unsigned long allowedMgf;
+
+			switch(CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg) {
+				case CKM_SHA_1:
+					pssParam.hashAlg = HashAlgo::SHA1;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA1;
+					allowedMgf = CKG_MGF1_SHA1;
+					break;
+				case CKM_SHA224:
+					pssParam.hashAlg = HashAlgo::SHA224;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA224;
+					allowedMgf = CKG_MGF1_SHA224;
+					break;
+				case CKM_SHA256:
+					pssParam.hashAlg = HashAlgo::SHA256;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA256;
+					allowedMgf = CKG_MGF1_SHA256;
+					break;
+				case CKM_SHA384:
+					pssParam.hashAlg = HashAlgo::SHA384;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA384;
+					allowedMgf = CKG_MGF1_SHA384;
+					break;
+				case CKM_SHA512:
+					pssParam.hashAlg = HashAlgo::SHA512;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA512;
+					allowedMgf = CKG_MGF1_SHA512;
+					break;
+				default:
+					ERROR_MSG("Invalid RSA-PSS hash");
+					return CKR_ARGUMENTS_BAD;
+			}
+
+			if (CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != allowedMgf) {
+				ERROR_MSG("Hash and MGF don't match");
+				return CKR_ARGUMENTS_BAD;
+			}
+
+			pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen;
+			param = &pssParam;
+			paramLen = sizeof(pssParam);
+			bAllowMultiPartOp = false;
+			isRSA = true;
+			break;
 		case CKM_SHA1_RSA_PKCS_PSS:
 			if (pMechanism->pParameter == NULL_PTR ||
 			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) ||
@@ -4593,6 +4655,56 @@ CK_RV SoftHSM::AsymVerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMech
 			bAllowMultiPartOp = true;
 			isRSA = true;
 			break;
+		case CKM_RSA_PKCS_PSS:
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS))
+			{
+				ERROR_MSG("Invalid parameters");
+				return CKR_ARGUMENTS_BAD;
+			}
+			mechanism = AsymMech::RSA_PKCS_PSS;
+
+			unsigned long expectedMgf;
+			switch(CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg) {
+				case CKM_SHA_1:
+					pssParam.hashAlg = HashAlgo::SHA1;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA1;
+					expectedMgf = CKG_MGF1_SHA1;
+					break;
+				case CKM_SHA224:
+					pssParam.hashAlg = HashAlgo::SHA224;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA224;
+					expectedMgf = CKG_MGF1_SHA224;
+					break;
+				case CKM_SHA256:
+					pssParam.hashAlg = HashAlgo::SHA256;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA256;
+					expectedMgf = CKG_MGF1_SHA256;
+					break;
+				case CKM_SHA384:
+					pssParam.hashAlg = HashAlgo::SHA384;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA384;
+					expectedMgf = CKG_MGF1_SHA384;
+					break;
+				case CKM_SHA512:
+					pssParam.hashAlg = HashAlgo::SHA512;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA512;
+					expectedMgf = CKG_MGF1_SHA512;
+					break;
+				default:
+					return CKR_ARGUMENTS_BAD;
+			}
+
+			if (CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != expectedMgf) {
+				return CKR_ARGUMENTS_BAD;
+			}
+
+			pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen;
+			param = &pssParam;
+			paramLen = sizeof(pssParam);
+			bAllowMultiPartOp = false;
+			isRSA = true;
+			break;
 		case CKM_SHA1_RSA_PKCS_PSS:
 			if (pMechanism->pParameter == NULL_PTR ||
 			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) ||
diff --git a/src/lib/crypto/AsymmetricAlgorithm.h b/src/lib/crypto/AsymmetricAlgorithm.h
index 54ac78d5..ca0d8400 100644
--- a/src/lib/crypto/AsymmetricAlgorithm.h
+++ b/src/lib/crypto/AsymmetricAlgorithm.h
@@ -70,6 +70,7 @@ struct AsymMech
 		RSA_SHA256_PKCS,
 		RSA_SHA384_PKCS,
 		RSA_SHA512_PKCS,
+		RSA_PKCS_PSS,
 		RSA_SHA1_PKCS_PSS,
 		RSA_SHA224_PKCS_PSS,
 		RSA_SHA256_PKCS_PSS,
diff --git a/src/lib/crypto/OSSLRSA.cpp b/src/lib/crypto/OSSLRSA.cpp
index 91b6466c..1e5638a0 100644
--- a/src/lib/crypto/OSSLRSA.cpp
+++ b/src/lib/crypto/OSSLRSA.cpp
@@ -121,6 +121,112 @@ bool OSSLRSA::sign(PrivateKey* privateKey, const ByteString& dataToSign,
 
 		return true;
 	}
+	else if (mechanism == AsymMech::RSA_PKCS_PSS)
+	{
+		const RSA_PKCS_PSS_PARAMS *pssParam = (RSA_PKCS_PSS_PARAMS*)param;
+
+		// Separate implementation for RSA PKCS #1 signing without hash computation
+
+		// Check if the private key is the right type
+		if (!privateKey->isOfType(OSSLRSAPrivateKey::type))
+		{
+			ERROR_MSG("Invalid key type supplied");
+
+			return false;
+		}
+
+		if (pssParam == NULL || paramLen != sizeof(RSA_PKCS_PSS_PARAMS))
+		{
+			ERROR_MSG("Invalid parameters supplied");
+
+			return false;
+		}
+
+		size_t allowedLen;
+		const EVP_MD* hash = NULL;
+
+		switch (pssParam->hashAlg)
+		{
+		case HashAlgo::SHA1:
+			hash = EVP_sha1();
+			allowedLen = 20;
+			break;
+		case HashAlgo::SHA224:
+			hash = EVP_sha224();
+			allowedLen = 28;
+			break;
+		case HashAlgo::SHA256:
+			hash = EVP_sha256();
+			allowedLen = 32;
+			break;
+		case HashAlgo::SHA384:
+			hash = EVP_sha384();
+			allowedLen = 48;
+			break;
+		case HashAlgo::SHA512:
+			hash = EVP_sha512();
+			allowedLen = 64;
+			break;
+		default:
+			return false;
+		}
+
+		OSSLRSAPrivateKey* osslKey = (OSSLRSAPrivateKey*) privateKey;
+
+		RSA* rsa = osslKey->getOSSLKey();
+
+		if (dataToSign.size() != allowedLen)
+		{
+			ERROR_MSG("Data to sign does not match expected (%d) for RSA PSS", (int)allowedLen);
+
+			return false;
+		}
+
+		size_t sLen = pssParam->sLen;
+		if (sLen > ((privateKey->getBitLength()+6)/8-2-allowedLen))
+		{
+			ERROR_MSG("sLen (%lu) is too large for current key size (%lu)",
+				  (unsigned long)sLen, privateKey->getBitLength());
+			return false;
+		}
+
+		ByteString em;
+		em.resize(osslKey->getN().size());
+
+		int status = RSA_padding_add_PKCS1_PSS_mgf1(rsa, &em[0], (unsigned char*) dataToSign.const_byte_str(), hash, hash, pssParam->sLen);
+		if (!status)
+		{
+			ERROR_MSG("Error in RSA PSS padding generation");
+
+			return false;
+		}
+
+
+		if (!RSA_blinding_on(rsa, NULL))
+		{
+			ERROR_MSG("Failed to turn on blinding for OpenSSL RSA key");
+
+			return false;
+		}
+
+		// Perform the signature operation
+		signature.resize(osslKey->getN().size());
+
+		int sigLen = RSA_private_encrypt(osslKey->getN().size(), &em[0], &signature[0], rsa, RSA_NO_PADDING);
+
+		RSA_blinding_off(rsa);
+
+		if (sigLen == -1)
+		{
+			ERROR_MSG("An error occurred while performing the RSA-PSS signature");
+
+			return false;
+		}
+
+		signature.resize(sigLen);
+
+		return true;
+	}
 	else if (mechanism == AsymMech::RSA)
 	{
 		// Separate implementation for raw RSA signing
@@ -607,6 +713,90 @@ bool OSSLRSA::verify(PublicKey* publicKey, const ByteString& originalData,
 
 		return (originalData == recoveredData);
 	}
+	else if (mechanism == AsymMech::RSA_PKCS_PSS)
+	{
+		const RSA_PKCS_PSS_PARAMS *pssParam = (RSA_PKCS_PSS_PARAMS*)param;
+
+		if (pssParam == NULL || paramLen != sizeof(RSA_PKCS_PSS_PARAMS))
+		{
+			ERROR_MSG("Invalid parameters supplied");
+
+			return false;
+		}
+
+		// Check if the public key is the right type
+		if (!publicKey->isOfType(OSSLRSAPublicKey::type))
+		{
+			ERROR_MSG("Invalid key type supplied");
+
+			return false;
+		}
+
+		// Perform the RSA public key operation
+		OSSLRSAPublicKey* osslKey = (OSSLRSAPublicKey*) publicKey;
+
+		ByteString recoveredData;
+
+		recoveredData.resize(osslKey->getN().size());
+
+		RSA* rsa = osslKey->getOSSLKey();
+
+		int retLen = RSA_public_decrypt(signature.size(), (unsigned char*) signature.const_byte_str(), &recoveredData[0], rsa, RSA_NO_PADDING);
+
+		if (retLen == -1)
+		{
+			ERROR_MSG("Public key operation failed");
+
+			return false;
+		}
+
+		recoveredData.resize(retLen);
+
+		size_t allowedLen;
+		const EVP_MD* hash = NULL;
+
+		switch (pssParam->hashAlg)
+		{
+		case HashAlgo::SHA1:
+			hash = EVP_sha1();
+			allowedLen = 20;
+			break;
+		case HashAlgo::SHA224:
+			hash = EVP_sha224();
+			allowedLen = 28;
+			break;
+		case HashAlgo::SHA256:
+			hash = EVP_sha256();
+			allowedLen = 32;
+			break;
+		case HashAlgo::SHA384:
+			hash = EVP_sha384();
+			allowedLen = 48;
+			break;
+		case HashAlgo::SHA512:
+			hash = EVP_sha512();
+			allowedLen = 64;
+			break;
+		default:
+			return false;
+		}
+
+		if (originalData.size() != allowedLen) {
+			return false;
+		}
+
+		size_t sLen = pssParam->sLen;
+		if (sLen > ((osslKey->getBitLength()+6)/8-2-allowedLen))
+		{
+			ERROR_MSG("sLen (%lu) is too large for current key size (%lu)",
+				  (unsigned long)sLen, osslKey->getBitLength());
+			return false;
+		}
+
+		int status = RSA_verify_PKCS1_PSS_mgf1(rsa, (unsigned char*)originalData.const_byte_str(), hash, hash, (unsigned char*) recoveredData.const_byte_str(), pssParam->sLen);
+
+		return (status == 1);
+	}
 	else if (mechanism == AsymMech::RSA)
 	{
 		// Specific implementation for raw RSA verifiction; originalData is assumed to contain the
diff --git a/src/lib/test/SignVerifyTests.cpp b/src/lib/test/SignVerifyTests.cpp
index 4536d7aa..46ebfb61 100644
--- a/src/lib/test/SignVerifyTests.cpp
+++ b/src/lib/test/SignVerifyTests.cpp
@@ -195,6 +195,44 @@ void SignVerifyTests::signVerifySingle(CK_MECHANISM_TYPE mechanismType, CK_SESSI
 	CPPUNIT_ASSERT(rv==CKR_SIGNATURE_INVALID);
 }
 
+void SignVerifyTests::signVerifySingleData(size_t dataSize, CK_MECHANISM_TYPE mechanismType, CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hPublicKey, CK_OBJECT_HANDLE hPrivateKey, CK_VOID_PTR param /* = NULL_PTR */, CK_ULONG paramLen /* = 0 */)
+{
+	CK_RV rv;
+	CK_MECHANISM mechanism = { mechanismType, param, paramLen };
+	CK_BYTE *data = (CK_BYTE*)malloc(dataSize);
+	CK_BYTE signature[1024];
+	CK_ULONG ulSignatureLen = 0;
+	unsigned i;
+
+	CPPUNIT_ASSERT(data != NULL);
+
+	for (i=0;i<dataSize;i++)
+		data[i] = i;
+
+	rv = CRYPTOKI_F_PTR( C_SignInit(hSession,&mechanism,hPrivateKey) );
+	CPPUNIT_ASSERT(rv==CKR_OK);
+
+	ulSignatureLen = sizeof(signature);
+	rv = CRYPTOKI_F_PTR( C_Sign(hSession,data,dataSize,signature,&ulSignatureLen) );
+	CPPUNIT_ASSERT(rv==CKR_OK);
+
+	rv = CRYPTOKI_F_PTR( C_VerifyInit(hSession,&mechanism,hPublicKey) );
+	CPPUNIT_ASSERT(rv==CKR_OK);
+
+	rv = CRYPTOKI_F_PTR( C_Verify(hSession,data,dataSize,signature,ulSignatureLen) );
+	CPPUNIT_ASSERT(rv==CKR_OK);
+
+	// verify again, but now change the input that is being signed.
+	rv = CRYPTOKI_F_PTR( C_VerifyInit(hSession,&mechanism,hPublicKey) );
+	CPPUNIT_ASSERT(rv==CKR_OK);
+
+	data[0] = 0xff;
+	rv = CRYPTOKI_F_PTR( C_Verify(hSession,data,dataSize,signature,ulSignatureLen) );
+	CPPUNIT_ASSERT(rv==CKR_SIGNATURE_INVALID);
+
+	free(data);
+}
+
 void SignVerifyTests::signVerifyMulti(CK_MECHANISM_TYPE mechanismType, CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hPublicKey, CK_OBJECT_HANDLE hPrivateKey, CK_VOID_PTR param /* = NULL_PTR */, CK_ULONG paramLen /* = 0 */)
 {
 	CK_RV rv;
@@ -287,6 +325,15 @@ void SignVerifyTests::testRsaSignVerify()
 	signVerifyMulti(CKM_SHA256_RSA_PKCS, hSessionRO, hPuk,hPrk);
 	signVerifyMulti(CKM_SHA384_RSA_PKCS, hSessionRO, hPuk,hPrk);
 	signVerifyMulti(CKM_SHA512_RSA_PKCS, hSessionRO, hPuk,hPrk);
+
+#ifdef WITH_OPENSSL
+	signVerifySingleData(20, CKM_RSA_PKCS_PSS, hSessionRO, hPuk,hPrk, &params[0], sizeof(params[0]));
+	signVerifySingleData(28, CKM_RSA_PKCS_PSS, hSessionRO, hPuk,hPrk, &params[1], sizeof(params[1]));
+	signVerifySingleData(32, CKM_RSA_PKCS_PSS, hSessionRO, hPuk,hPrk, &params[2], sizeof(params[2]));
+	signVerifySingleData(48, CKM_RSA_PKCS_PSS, hSessionRO, hPuk,hPrk, &params[3], sizeof(params[3]));
+	signVerifySingleData(64, CKM_RSA_PKCS_PSS, hSessionRO, hPuk,hPrk, &params[4], sizeof(params[4]));
+#endif
+
 	signVerifyMulti(CKM_SHA1_RSA_PKCS_PSS, hSessionRO, hPuk,hPrk, &params[0], sizeof(params[0]));
 	signVerifyMulti(CKM_SHA224_RSA_PKCS_PSS, hSessionRO, hPuk,hPrk, &params[1], sizeof(params[1]));
 	signVerifyMulti(CKM_SHA256_RSA_PKCS_PSS, hSessionRO, hPuk,hPrk, &params[2], sizeof(params[2]));
diff --git a/src/lib/test/SignVerifyTests.h b/src/lib/test/SignVerifyTests.h
index dc645ef6..0fa6a145 100644
--- a/src/lib/test/SignVerifyTests.h
+++ b/src/lib/test/SignVerifyTests.h
@@ -61,6 +61,7 @@ class SignVerifyTests : public TestsBase
 	CK_RV generateEC(const char* curve, CK_SESSION_HANDLE hSession, CK_BBOOL bTokenPuk, CK_BBOOL bPrivatePuk, CK_BBOOL bTokenPrk, CK_BBOOL bPrivatePrk, CK_OBJECT_HANDLE &hPuk, CK_OBJECT_HANDLE &hPrk);
 #endif
 	void signVerifySingle(CK_MECHANISM_TYPE mechanismType, CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hPublicKey, CK_OBJECT_HANDLE hPrivateKey, CK_VOID_PTR param = NULL_PTR, CK_ULONG paramLen = 0);
+	void signVerifySingleData(size_t dataSize, CK_MECHANISM_TYPE mechanismType, CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hPublicKey, CK_OBJECT_HANDLE hPrivateKey, CK_VOID_PTR param = NULL_PTR, CK_ULONG paramLen = 0);
 	void signVerifyMulti(CK_MECHANISM_TYPE mechanismType, CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hPublicKey, CK_OBJECT_HANDLE hPrivateKey, CK_VOID_PTR param = NULL_PTR, CK_ULONG paramLen = 0);
 	CK_RV generateKey(CK_SESSION_HANDLE hSession, CK_KEY_TYPE keyType, CK_BBOOL bToken, CK_BBOOL bPrivate, CK_OBJECT_HANDLE &hKey);
 	void hmacSignVerify(CK_MECHANISM_TYPE mechanismType, CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey);