From 5207cea43635018218610180e638054f5a9648a7 Mon Sep 17 00:00:00 2001 From: Krzysztof Jackiewicz Date: Fri, 22 Sep 2023 11:34:00 +0200 Subject: [PATCH] Implement hash selection in RSA OAEP SW and TZ backend implementation Unit-tests included Change-Id: I6efd8a0477fe3137491c889d0c70eabc0d861097 --- src/include/ckm/ckm-type.h | 1 + src/include/ckmc/ckmc-type.h | 10 ++- src/manager/common/protocols.cpp | 1 + src/manager/crypto/sw-backend/internals.cpp | 103 +++++++++++++++------------ src/manager/crypto/tz-backend/internals.cpp | 30 +++++++- src/manager/crypto/tz-backend/tz-context.cpp | 66 +++++++++++------ src/manager/crypto/tz-backend/tz-context.h | 3 + unit-tests/test_sw-backend.cpp | 31 ++++++-- 8 files changed, 169 insertions(+), 76 deletions(-) diff --git a/src/include/ckm/ckm-type.h b/src/include/ckm/ckm-type.h index 822da90..a7d2c7b 100644 --- a/src/include/ckm/ckm-type.h +++ b/src/include/ckm/ckm-type.h @@ -188,6 +188,7 @@ enum class ParamName : int { ED_AAD, ED_TAG_LEN, ED_LABEL, + ED_OAEP_HASH, // key generation GEN_KEY_LEN = 201, diff --git a/src/include/ckmc/ckmc-type.h b/src/include/ckmc/ckmc-type.h index 1602f62..3de4c1a 100644 --- a/src/include/ckmc/ckmc-type.h +++ b/src/include/ckmc/ckmc-type.h @@ -298,6 +298,10 @@ typedef enum __ckmc_param_name { CKMC_PARAM_ED_AAD, /**< buffer - Additional Authentication Data for AES GCM */ CKMC_PARAM_ED_TAG_LEN, /**< integer - tag length in bits */ CKMC_PARAM_ED_LABEL, /**< buffer - RSA OAEP label (not supported at the moment) */ + CKMC_PARAM_ED_OAEP_HASH, /**< integer - function to be used both as Label and MGF hash function + in OAEP padding (see #__ckmc_hash_algo). Currently only #CKMC_HASH_SHA1 + and #CKMC_HASH_SHA256 are supported. If not given, the default + #CKMC_HASH_SHA1 is used. (Since 6.0) */ CKMC_PARAM_KDF_PRF = 401, /**< integer - pseudo-random function number (see #ckmc_kdf_prf_e) (Since 6.0) */ @@ -434,11 +438,13 @@ typedef enum __ckmc_algo_type { - #CKMC_PARAM_ALGO_TYPE = #CKMC_ALGO_AES_CFB (mandatory), - #CKMC_PARAM_ED_IV = 16-byte initialization vector (mandatory) */ - CKMC_ALGO_RSA_OAEP, /**< RSA-OAEP algorithm (EME-OAEP as defined in PKCS #1 with SHA-1 and MGF1) + CKMC_ALGO_RSA_OAEP, /**< RSA-OAEP algorithm (EME-OAEP as defined in PKCS #1 with MGF1) Supported parameters: - #CKMC_PARAM_ALGO_TYPE = #CKMC_ALGO_RSA_OAEP (mandatory), - #CKMC_PARAM_ED_LABEL = label (encoding parameter) to be associated with - the message (optional, not supported at the moment) */ + the message (optional, not supported at the moment) + - #CKMC_PARAM_ED_OAEP_HASH = hash algorithm to be used in OAEP padding (see + #__ckmc_hash_algo). (optional) */ CKMC_ALGO_KBKDF, /**< Key based key derivation algorithm Supported parameters: diff --git a/src/manager/common/protocols.cpp b/src/manager/common/protocols.cpp index d270f0c..417a65c 100644 --- a/src/manager/common/protocols.cpp +++ b/src/manager/common/protocols.cpp @@ -233,6 +233,7 @@ void CryptoAlgorithmSerializable::Deserialize(IStream &stream) case ParamName::ALGO_TYPE: case ParamName::ED_CTR_LEN: case ParamName::ED_TAG_LEN: + case ParamName::ED_OAEP_HASH: case ParamName::GEN_KEY_LEN: case ParamName::GEN_EC: case ParamName::SV_HASH_ALGO: diff --git a/src/manager/crypto/sw-backend/internals.cpp b/src/manager/crypto/sw-backend/internals.cpp index 77d1252..17b8cad 100644 --- a/src/manager/crypto/sw-backend/internals.cpp +++ b/src/manager/crypto/sw-backend/internals.cpp @@ -237,6 +237,10 @@ typedef ParamCheck::Equals<0, 8, 16, 24, 32>> KbkdfLlenCheck; +typedef ParamCheck::Equals> OaepHashAlgoCheck; typedef std::map ValidatorMap; ValidatorMap initValidators() @@ -253,7 +257,7 @@ ValidatorMap initValidators() validators.emplace(AlgoType::AES_CBC, VBuilder::Build()); validators.emplace(AlgoType::AES_CFB, VBuilder::Build()); validators.emplace(AlgoType::AES_GCM, VBuilder::Build()); - validators.emplace(AlgoType::RSA_OAEP, VBuilder::Build()); + validators.emplace(AlgoType::RSA_OAEP, VBuilder::Build()); validators.emplace(AlgoType::ECDH, VBuilder::Build()); validators.emplace(AlgoType::KBKDF, VBuilder(alg); - - if (!pkey) - ThrowErr(Exc::Crypto::InputParam, logPrefix, "no key"); - - RSA *rsa = EVP_PKEY_get1_RSA(pkey.get()); - - if (!rsa) - ThrowErr(Exc::Crypto::InputParam, logPrefix, "invalid key"); - - /* - * RSA_padding_add_PKCS1_OAEP supports custom label but RSA_public_encrypt calls it with NULL - * value so for now label is not supported. Alternative is to rewrite the openssl implementation - * to support it: openssl-fips/crypto/rsa/rsa_eay.c - */ - RawBuffer output; - output.resize(RSA_size(rsa)); - int ret; - try { - ERR_clear_error(); - ret = cryptoFn(data.size(), - data.data(), - output.data(), - rsa, - RSA_PKCS1_OAEP_PADDING); - if (ret < 0) - errorHandle(__FILE__, __LINE__, __func__, ret); - } catch (...) { - RSA_free(rsa); - throw; - } - - output.resize(ret); - return output; -} - const EVP_MD *getMdAlgo(const HashAlgorithm hashAlgo) { switch (hashAlgo) { @@ -450,6 +410,55 @@ EvpPkeyCtxUPtr newCtx(EVP_PKEY *pkey) ThrowErr(Exc::Crypto::InternalError, "Error in EVP_PKEY_CTX_new function"); } +RawBuffer asymmetricHelper( + int (*initFn)(EVP_PKEY_CTX *), + int (*cryptFn)(EVP_PKEY_CTX *, unsigned char *, size_t *, const unsigned char *, size_t), + const EvpShPtr &pkey, + const CryptoAlgorithm &alg, + const RawBuffer &data) +{ + int ret; + size_t outlen; + + validateParams(alg); + + HashAlgorithm hash = HashAlgorithm::SHA1; + alg.getParam(ParamName::ED_OAEP_HASH, hash); + + if (!pkey) + ThrowErr(Exc::Crypto::InputParam, "no key"); + + if (EVP_PKEY_base_id(pkey.get()) != EVP_PKEY_RSA) + ThrowErr(Exc::Crypto::InputParam, "Wrong key type"); + + auto ctx = newCtx(pkey.get()); + + ret = (*initFn)(ctx.get()); + if (ret <= 0) + errorHandle(__FILE__, __LINE__, __func__, ret); + + if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) <= 0) + ThrowErr(Exc::Crypto::InternalError, "EVP_PKEY_CTX_set_rsa_padding failed"); + + if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), getMdAlgo(hash)) <= 0) + ThrowErr(Exc::Crypto::InternalError, "EVP_PKEY_CTX_set_rsa_oaep_md failed"); + + // TODO set label with EVP_PKEY_CTX_set0_rsa_oaep_label + + ret = (*cryptFn)(ctx.get(), NULL, &outlen, data.data(), data.size()); + if (ret <= 0) + errorHandle(__FILE__, __LINE__, __func__, ret); + + RawBuffer output(outlen); + + ret = (*cryptFn)(ctx.get(), output.data(), &outlen, data.data(), data.size()); + if (ret <= 0) + errorHandle(__FILE__, __LINE__, __func__, ret); + + output.resize(outlen); + return output; +} + DataPair keyPair(const EvpPkeyCtxUPtr &ctx, KeyType prv, KeyType pub) { EVP_PKEY *pkeyTmp = NULL; @@ -926,14 +935,14 @@ RawBuffer asymmetricEncrypt(const EvpShPtr &pkey, const CryptoAlgorithm &alg, const RawBuffer &data) { - return asymmetricHelper(RSA_public_encrypt, "Asymmetric encryption: ", pkey, alg, data); + return asymmetricHelper(EVP_PKEY_encrypt_init, EVP_PKEY_encrypt, pkey, alg, data); } RawBuffer asymmetricDecrypt(const EvpShPtr &pkey, const CryptoAlgorithm &alg, const RawBuffer &data) { - return asymmetricHelper(RSA_private_decrypt, "Asymmetric decryption: ", pkey, alg, data); + return asymmetricHelper(EVP_PKEY_decrypt_init, EVP_PKEY_decrypt, pkey, alg, data); } RawBuffer sign(EVP_PKEY *pkey, diff --git a/src/manager/crypto/tz-backend/internals.cpp b/src/manager/crypto/tz-backend/internals.cpp index 91098a4..c930544 100644 --- a/src/manager/crypto/tz-backend/internals.cpp +++ b/src/manager/crypto/tz-backend/internals.cpp @@ -197,9 +197,11 @@ void decompose(const CryptoAlgorithm &alg, AlgoType &algo, uint32_t &ctrLenOrTagSizeBits, RawBuffer &iv, - RawBuffer &aad) + RawBuffer &aad, + HashAlgorithm &oaepHash) { algo = unpack(alg, ParamName::ALGO_TYPE); + oaepHash = HashAlgorithm::SHA1; switch (algo) { case AlgoType::AES_CTR: iv = unpack(alg, ParamName::ED_IV); @@ -224,6 +226,9 @@ void decompose(const CryptoAlgorithm &alg, alg.getParam(ParamName::ED_AAD, aad); break; case AlgoType::RSA_OAEP: + alg.getParam(ParamName::ED_OAEP_HASH, oaepHash); + if (oaepHash == HashAlgorithm::NONE) + ThrowErr(Exc::Crypto::InputParam, "Invalid OAEP hash"); break; default: ThrowErr(Exc::Crypto::InputParam, "Invalid decryption algorithm"); @@ -383,12 +388,14 @@ void importWrappedKey(const RawBuffer &wrappingKeyId, uint32_t ctrLenOrTagSizeBits = 0; RawBuffer iv; RawBuffer aad; - decompose(alg, algo, ctrLenOrTagSizeBits, iv, aad); + HashAlgorithm oaepHash; + decompose(alg, algo, ctrLenOrTagSizeBits, iv, aad, oaepHash); // TODO it is awful! TrustZoneContext::Instance().importWrappedKey(wrappingKeyId, wrappingKeyPwd, getAlgType(algo), + getHashType(oaepHash), iv, ctrLenOrTagSizeBits, aad, @@ -411,18 +418,21 @@ RawBuffer exportWrappedKey(const RawBuffer &wrappingKeyId, uint32_t ctrLenOrTagSizeBits = 0; RawBuffer iv; RawBuffer aad; - decompose(alg, algo, ctrLenOrTagSizeBits, iv, aad); + HashAlgorithm oaepHash; + decompose(alg, algo, ctrLenOrTagSizeBits, iv, aad, oaepHash); // TODO it is awful! return TrustZoneContext::Instance().exportWrappedKey(wrappingKeyId, wrappingKeyPwd, getAlgType(algo), + getHashType(oaepHash), iv, ctrLenOrTagSizeBits, aad, keyToWrapId, keyToWrapPwd, toTzDataType(keyToWrapType)); + } RawBuffer getData(const RawBuffer &dataId, @@ -533,6 +543,7 @@ RawBuffer symmetricEncrypt(const RawBuffer &keyId, RawBuffer result; TrustZoneContext::Instance().executeCrypt(CMD_ENCRYPT, getAlgType(algo), + HASH_SHA1, // ignored keyId, pwd, unpack(alg, ParamName::ED_IV), @@ -584,6 +595,7 @@ RawBuffer symmetricDecrypt(const RawBuffer &keyId, RawBuffer result; TrustZoneContext::Instance().executeCrypt(CMD_DECRYPT, getAlgType(algo), + HASH_SHA1, // ignored keyId, pwd, unpack(alg, ParamName::ED_IV), @@ -617,6 +629,11 @@ RawBuffer asymmetricEncrypt(const RawBuffer &keyId, const RawBuffer &data) { AlgoType algo = unpack(alg, ParamName::ALGO_TYPE); + HashAlgorithm hash = HashAlgorithm::SHA1; + alg.getParam(ParamName::ED_OAEP_HASH, hash); + if (hash == HashAlgorithm::NONE) + ThrowErr(Exc::Crypto::InputParam, "Invalid OAEP hash"); + RawBuffer result; switch (algo) @@ -624,6 +641,7 @@ RawBuffer asymmetricEncrypt(const RawBuffer &keyId, case AlgoType::RSA_OAEP: { TrustZoneContext::Instance().executeCrypt(CMD_ENCRYPT, getAlgType(algo), + getHashType(hash), keyId, pwd, result, // unused dummy @@ -645,6 +663,11 @@ RawBuffer asymmetricDecrypt(const RawBuffer &keyId, const RawBuffer &cipher) { AlgoType algo = unpack(alg, ParamName::ALGO_TYPE); + HashAlgorithm hash = HashAlgorithm::SHA1; + alg.getParam(ParamName::ED_OAEP_HASH, hash); + if (hash != HashAlgorithm::SHA1 && hash != HashAlgorithm::SHA256) + ThrowErr(Exc::Crypto::InputParam, "Invalid OAEP hash"); + RawBuffer result; switch (algo) @@ -652,6 +675,7 @@ RawBuffer asymmetricDecrypt(const RawBuffer &keyId, case AlgoType::RSA_OAEP: { TrustZoneContext::Instance().executeCrypt(CMD_DECRYPT, getAlgType(algo), + getHashType(hash), keyId, pwd, result, // unused dummy diff --git a/src/manager/crypto/tz-backend/tz-context.cpp b/src/manager/crypto/tz-backend/tz-context.cpp index 9bfc390..fd83d98 100644 --- a/src/manager/crypto/tz-backend/tz-context.cpp +++ b/src/manager/crypto/tz-backend/tz-context.cpp @@ -367,6 +367,7 @@ void TrustZoneContext::generateECKey(tz_ec ec, void TrustZoneContext::executeCrypt(tz_command cmd, tz_algo_type algo, + tz_hash_type hash, const RawBuffer &keyId, const Pwd &pwd, const RawBuffer &iv, @@ -381,7 +382,7 @@ void TrustZoneContext::executeCrypt(tz_command cmd, TZSerializer sIn; if (algo == ALGO_RSA) - sIn = makeSerializer(data, pwd, keyId); + sIn = makeSerializer(data, pwd, hash, keyId); else sIn = makeSerializer(data, pwd, iv, keyId); @@ -699,6 +700,7 @@ void TrustZoneContext::importData( void TrustZoneContext::importWrappedKey(const RawBuffer &wrappingKeyId, const Pwd &wrappingKeyPwd, tz_algo_type algo, + tz_hash_type oaepHash, const RawBuffer &iv, const uint32_t ctrLenOrTagSizeBits, const RawBuffer &aad, @@ -712,16 +714,28 @@ void TrustZoneContext::importWrappedKey(const RawBuffer &wrappingKeyId, // command ID = CMD_IMPORT_WRAPPED_KEY LogDebug("TrustZoneContext::importWrappedKey encryptedKey size = [" << encryptedKey.size() << "]"); - auto sIn = makeSerializer(wrappingKeyId, - wrappingKeyPwd, - algo, - iv, - ctrLenOrTagSizeBits, - aad, - encryptedKeyType, - encryptedKey, - EncPwd{encryptedKeyPwdBuf, encryptedKeyIV}, - encryptedKeyId); + TZSerializer sIn; + if (algo == ALGO_RSA) { + sIn = makeSerializer(wrappingKeyId, + wrappingKeyPwd, + algo, + oaepHash, + encryptedKeyType, + encryptedKey, + EncPwd{encryptedKeyPwdBuf, encryptedKeyIV}, + encryptedKeyId); + } else { + sIn = makeSerializer(wrappingKeyId, + wrappingKeyPwd, + algo, + iv, + ctrLenOrTagSizeBits, + aad, + encryptedKeyType, + encryptedKey, + EncPwd{encryptedKeyPwdBuf, encryptedKeyIV}, + encryptedKeyId); + } TrustZoneMemory inMemory(m_Context, sIn.GetSize(), TEEC_MEM_INPUT); sIn.Serialize(inMemory); @@ -750,6 +764,7 @@ void TrustZoneContext::importWrappedKey(const RawBuffer &wrappingKeyId, RawBuffer TrustZoneContext::exportWrappedKey(const RawBuffer &wrappingKeyId, const Pwd &wrappingKeyPwd, tz_algo_type algo, + tz_hash_type oaepHash, const RawBuffer &iv, const uint32_t ctrLenOrTagSizeBits, const RawBuffer &aad, @@ -760,15 +775,26 @@ RawBuffer TrustZoneContext::exportWrappedKey(const RawBuffer &wrappingKeyId, // command ID = CMD_EXPORT_WRAPPED_KEY LogDebug("TrustZoneContext::exportWrappedKey"); - auto sIn = makeSerializer(wrappingKeyId, - wrappingKeyPwd, - algo, - iv, - ctrLenOrTagSizeBits, - aad, - keyToWrapId, - keyToWrapPwd, - keyToWrapType); + TZSerializer sIn; + if (algo == ALGO_RSA) { + sIn = makeSerializer(wrappingKeyId, + wrappingKeyPwd, + algo, + oaepHash, + keyToWrapId, + keyToWrapPwd, + keyToWrapType); + } else { + sIn = makeSerializer(wrappingKeyId, + wrappingKeyPwd, + algo, + iv, + ctrLenOrTagSizeBits, + aad, + keyToWrapId, + keyToWrapPwd, + keyToWrapType); + } TrustZoneMemory inMemory(m_Context, sIn.GetSize(), TEEC_MEM_INPUT); sIn.Serialize(inMemory); diff --git a/src/manager/crypto/tz-backend/tz-context.h b/src/manager/crypto/tz-backend/tz-context.h index 2033cf7..83d7650 100644 --- a/src/manager/crypto/tz-backend/tz-context.h +++ b/src/manager/crypto/tz-backend/tz-context.h @@ -92,6 +92,7 @@ public: void importWrappedKey(const RawBuffer &wrappingKeyId, const Pwd &wrappingKeyPwd, tz_algo_type algo, + tz_hash_type oaepHash, const RawBuffer &iv, const uint32_t ctrLenOrTagSizeBits, const RawBuffer &aad, @@ -105,6 +106,7 @@ public: RawBuffer exportWrappedKey(const RawBuffer &wrappingKeyId, const Pwd &wrappingKeyPwd, tz_algo_type algo, + tz_hash_type oaepHash, const RawBuffer &iv, const uint32_t ctrLenOrTagSizeBits, const RawBuffer &aad, @@ -114,6 +116,7 @@ public: void executeCrypt(tz_command cmd, tz_algo_type algo, + tz_hash_type hash, const RawBuffer &keyId, const Pwd &pwd, const RawBuffer &iv, diff --git a/unit-tests/test_sw-backend.cpp b/unit-tests/test_sw-backend.cpp index 866e3eb..488f560 100644 --- a/unit-tests/test_sw-backend.cpp +++ b/unit-tests/test_sw-backend.cpp @@ -344,6 +344,17 @@ private: GObjUPtr secret; }; +size_t oaepMaxSize(size_t key_bits, HashAlgorithm hash) +{ + switch (hash) { + case HashAlgorithm::SHA1: return key_bits / 8 - 42; + case HashAlgorithm::SHA256: return key_bits / 8 - 66; + case HashAlgorithm::SHA384: return key_bits / 8 - 98; + case HashAlgorithm::SHA512: return key_bits / 8 - 130; + default: BOOST_FAIL("Unsupported OAEP hash"); return 0; + } +} + } // namespace BOOST_AUTO_TEST_SUITE(SW_TEST) @@ -776,7 +787,7 @@ NEGATIVE_TEST_CASE(symmetricEncryptDecryptCbc) POSITIVE_TEST_CASE(asymmetricEncryptDecrypt) { - constexpr int KEY_BIT_LEN = 1024; + constexpr int KEY_BIT_LEN = 2048; auto& rsaKeys = generateObjUPtrPair(AlgoType::RSA_GEN, KEY_BIT_LEN); CryptoAlgorithm enc; @@ -794,9 +805,14 @@ POSITIVE_TEST_CASE(asymmetricEncryptDecrypt) BOOST_REQUIRE(decrypted == input); }; - encryptDecrypt(createRandom(KEY_BIT_LEN / 8 - 42)); - encryptDecrypt(createRandom(KEY_BIT_LEN / 8 - 42 - 1)); + encryptDecrypt(createRandom(oaepMaxSize(KEY_BIT_LEN, HashAlgorithm::SHA1))); + encryptDecrypt(createRandom(oaepMaxSize(KEY_BIT_LEN, HashAlgorithm::SHA1) - 1)); encryptDecrypt(RawBuffer()); + + enc.setParam(ParamName::ED_OAEP_HASH, HashAlgorithm::SHA1); + encryptDecrypt(createRandom(oaepMaxSize(KEY_BIT_LEN, HashAlgorithm::SHA1))); + enc.setParam(ParamName::ED_OAEP_HASH, HashAlgorithm::SHA256); + encryptDecrypt(createRandom(oaepMaxSize(KEY_BIT_LEN, HashAlgorithm::SHA256))); } NEGATIVE_TEST_CASE(asymmetricEncryptDecrypt) @@ -804,7 +820,7 @@ NEGATIVE_TEST_CASE(asymmetricEncryptDecrypt) constexpr int KEY_BIT_LEN = 1024; auto& rsaKeys = generateObjUPtrPair(AlgoType::RSA_GEN, KEY_BIT_LEN); auto& dsaKeys = generateObjUPtrPair(AlgoType::DSA_GEN, KEY_BIT_LEN); - const auto data = createRandom(KEY_BIT_LEN / 8 - 42); + const auto data = createRandom(oaepMaxSize(KEY_BIT_LEN, HashAlgorithm::SHA1)); auto longData = data; longData.push_back(0); @@ -828,6 +844,13 @@ NEGATIVE_TEST_CASE(asymmetricEncryptDecrypt) enc4.setParam(ParamName::ED_LABEL, RawBuffer(64)); BOOST_REQUIRE_THROW(rsaKeys.pub->encrypt(enc4, data), Exc::Crypto::InputParam); + CryptoAlgorithm enc5; + enc5.setParam(ParamName::ALGO_TYPE, AlgoType::RSA_OAEP); + enc5.setParam(ParamName::ED_OAEP_HASH, HashAlgorithm::NONE); + BOOST_REQUIRE_THROW(rsaKeys.pub->encrypt(enc5, data), Exc::Crypto::InputParam); + enc5.setParam(ParamName::ED_OAEP_HASH, static_cast(-1)); + BOOST_REQUIRE_THROW(rsaKeys.pub->encrypt(enc5, data), Exc::Crypto::InputParam); + RawBuffer encrypted; BOOST_REQUIRE_NO_THROW(encrypted = rsaKeys.pub->encrypt(enc, data)); RawBuffer shortEncrypted = encrypted; -- 2.7.4