From e12d04b256f1c856003ae88c5c3bfe07ce332fa7 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos 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