From: Krzysztof Jackiewicz Date: Mon, 13 Feb 2023 09:10:39 +0000 (+0100) Subject: KBKDF HMAC implementation in sw backend X-Git-Tag: accepted/tizen/unified/20230406.165733~5^2~17 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a6f9aec24eda2bfb5b6dac8f70749fcff7e0802c;p=platform%2Fcore%2Fsecurity%2Fkey-manager.git KBKDF HMAC implementation in sw backend Unit tests included Change-Id: If4287b38c61fe3842024c5e7baf4934685f92566 --- diff --git a/src/manager/crypto/sw-backend/internals.cpp b/src/manager/crypto/sw-backend/internals.cpp index e1e40a5..9796b2e 100644 --- a/src/manager/crypto/sw-backend/internals.cpp +++ b/src/manager/crypto/sw-backend/internals.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -45,8 +46,8 @@ #include #include #include - #include +#include "kbkdf.h" #define OPENSSL_SUCCESS 1 // DO NOT CHANGE THIS VALUE #define OPENSSL_FAIL 0 // DO NOT CHANGE THIS VALUE @@ -197,6 +198,46 @@ typedef ParamCheck> EcdhPubKeyCheck; +typedef ParamCheck::Equals> IsKbkdf; + +typedef ParamCheck::Equals> KdfPrfCheck; + +typedef ParamCheck::Equals> KbkdfModeCheck; + +typedef ParamCheck::Equals<16, 24, 32>> KdfLenCheck; + +typedef ParamCheck::Equals> KbkdfCounterLocationCheck; + +typedef ParamCheck::Equals<8, 16, 24, 32>> KbkdfRlenCheck; + +typedef ParamCheck::Equals<0, 8, 16, 24, 32>> KbkdfLlenCheck; + + typedef std::map ValidatorMap; ValidatorMap initValidators() { @@ -214,6 +255,12 @@ ValidatorMap initValidators() validators.emplace(AlgoType::AES_GCM, VBuilder::Build()); validators.emplace(AlgoType::RSA_OAEP, VBuilder::Build()); validators.emplace(AlgoType::ECDH, VBuilder::Build()); + validators.emplace(AlgoType::KBKDF, VBuilder::Build()); return validators; }; @@ -986,6 +1033,64 @@ Data deriveECDH(const EvpShPtr &pkey, const CryptoAlgorithm &alg) return { DataType::BINARY_DATA, std::move(secret)}; } +Data deriveKBKDF(const RawBuffer &secret, const CryptoAlgorithm &alg) +{ + validateParams(alg); + + RawBuffer label, context, fixed; + KbkdfCounterLocation counterLocation; + KdfPrf prf; + size_t length, rlenBits = 32, llenBits = 0, tmp; + bool hasLabel = alg.getParam(ParamName::KBKDF_LABEL, label); + bool hasContext = alg.getParam(ParamName::KBKDF_CONTEXT, context); + bool hasFixed = alg.getParam(ParamName::KBKDF_FIXED_INPUT, fixed); + alg.getParam(ParamName::KBKDF_COUNTER_LOCATION, counterLocation); + alg.getParam(ParamName::KDF_PRF, prf); + alg.getParam(ParamName::KDF_LEN, length); + alg.getParam(ParamName::KBKDF_RLEN, rlenBits); + alg.getParam(ParamName::KBKDF_LLEN, llenBits); + bool useSeparator = !alg.getParam(ParamName::KBKDF_NO_SEPARATOR, tmp); + + const EVP_MD* md = nullptr; + switch (prf) { + case KdfPrf::HMAC_SHA256: + md = EVP_sha256(); + break; + case KdfPrf::HMAC_SHA384: + md = EVP_sha384(); + break; + case KdfPrf::HMAC_SHA512: + md = EVP_sha512(); + break; + default: + assert(false); // prf is checked in validateParams above + } + + RawBuffer key; + if (hasFixed) { + if (hasLabel || hasContext || !useSeparator || llenBits > 0 || + counterLocation == KbkdfCounterLocation::MIDDLE_FIXED) + ThrowErr(Exc::Crypto::InputParam, "Unexpected parameters for fixed input mode."); + + key = deriveKbkdfHmac(secret, length * 8, md, counterLocation, rlenBits, fixed); + } else { + if (!hasLabel || !hasContext) + ThrowErr(Exc::Crypto::InputParam, "Missing label and/or context."); + + key = deriveKbkdfHmac(secret, + length * 8, + md, + counterLocation, + rlenBits, + llenBits, + label, + context, + useSeparator); + } + + return { DataType::KEY_AES, std::move(key)}; +} + } // namespace Internals } // namespace SW } // namespace Crypto diff --git a/src/manager/crypto/sw-backend/internals.h b/src/manager/crypto/sw-backend/internals.h index 52f8a2e..b2669a6 100644 --- a/src/manager/crypto/sw-backend/internals.h +++ b/src/manager/crypto/sw-backend/internals.h @@ -91,6 +91,7 @@ int verify(EVP_PKEY *pkey, const RawBuffer &signature); Data deriveECDH(const EvpShPtr &pkey, const CryptoAlgorithm &alg); +Data deriveKBKDF(const RawBuffer &secret, const CryptoAlgorithm &alg); } // namespace Internals } // namespace SW diff --git a/src/manager/crypto/sw-backend/obj.cpp b/src/manager/crypto/sw-backend/obj.cpp index f256eea..9b4e1a2 100644 --- a/src/manager/crypto/sw-backend/obj.cpp +++ b/src/manager/crypto/sw-backend/obj.cpp @@ -58,9 +58,11 @@ AlgoType key2algo(DataType type) } // namespace anonymous -Token BData::derive(const CryptoAlgorithm &, const Password &, const RawBuffer &) +Token BData::derive(const CryptoAlgorithm &alg, const Password &pass, const RawBuffer &) { - return Token(); + auto data = Internals::deriveKBKDF(getBinary(), alg); + + return Token(backendId(), data.type, Store::pack(data.buffer, pass)); } RawBuffer SKey::encrypt(const CryptoAlgorithm &alg, const RawBuffer &data) @@ -182,6 +184,11 @@ EvpShPtr Cert::getEvpShPtr() return m_evp; } +Token Cert::derive(const CryptoAlgorithm &, const Password &, const RawBuffer &) +{ + ThrowErr(Exc::Crypto::OperationNotSupported); +} + } // namespace SW } // namespace Crypto } // namespace CKM diff --git a/src/manager/crypto/sw-backend/obj.h b/src/manager/crypto/sw-backend/obj.h index ffb8231..2712341 100644 --- a/src/manager/crypto/sw-backend/obj.h +++ b/src/manager/crypto/sw-backend/obj.h @@ -82,6 +82,7 @@ class Cert : public AKey { public: Cert(CryptoBackend backendId, RawBuffer buffer, DataType dataType) : AKey(backendId, std::move(buffer), dataType) {} + Token derive(const CryptoAlgorithm &, const Password &, const RawBuffer &) override; protected: EvpShPtr getEvpShPtr() override; diff --git a/unit-tests/test_sw-backend.cpp b/unit-tests/test_sw-backend.cpp index f21e679..4de683e 100644 --- a/unit-tests/test_sw-backend.cpp +++ b/unit-tests/test_sw-backend.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -228,6 +229,121 @@ struct CertHelper : public Cert { using Cert::getEvpShPtr; }; +const std::optional NO_BUF; +const std::optional NO_SIZE; +const std::optional NO_PRF; +const std::optional NO_MODE; +const std::optional NO_LOC; + +constexpr KdfPrf HMAC256 = KdfPrf::HMAC_SHA256; +constexpr KdfPrf HMAC384 = KdfPrf::HMAC_SHA384; +constexpr KdfPrf HMAC512 = KdfPrf::HMAC_SHA512; +constexpr KbkdfCounterLocation BEFORE = KbkdfCounterLocation::BEFORE_FIXED; +constexpr KbkdfCounterLocation AFTER = KbkdfCounterLocation::AFTER_FIXED; +constexpr KbkdfCounterLocation MIDDLE = KbkdfCounterLocation::MIDDLE_FIXED; +constexpr KbkdfMode COUNTER = KbkdfMode::COUNTER; + +const RawBuffer CTX{'c','o','n','t','e','x','t'}; +const RawBuffer LAB{'l','a','b','e','l'}; +const RawBuffer FIX{'f','i','x','e','d'}; +const RawBuffer ONE(1); +const RawBuffer EMPTY; + +class KbkdfParamTester { +public: + KbkdfParamTester() { + Token token; + BOOST_REQUIRE_NO_THROW(token = STORE.import(Data(DataType::BINARY_DATA, RawBuffer(16)), + "", + EncryptionParams(), + RawBuffer())); + + BOOST_REQUIRE_NO_THROW(secret = STORE.getObject(token, "")); + } + + void Ok(const std::optional& len, + const std::optional& prf, + const std::optional& mode, + const std::optional& location, + const std::optional& context, + const std::optional& label, + const std::optional& fixed, + const std::optional& rlen, + const std::optional& llen, + bool noSeparator = false) + { + return Test(true, len, prf, mode, location, context, label, fixed, rlen, llen, noSeparator); + } + + void Fail(const std::optional& len, + const std::optional& prf, + const std::optional& mode, + const std::optional& location, + const std::optional& context, + const std::optional& label, + const std::optional& fixed, + const std::optional& rlen, + const std::optional& llen, + bool noSeparator = false) + { + return Test( + false, len, prf, mode, location, context, label, fixed, rlen, llen, noSeparator); + } +private: + void Test(bool ok, + const std::optional& len, + const std::optional& prf, + const std::optional& mode, + const std::optional& location, + const std::optional& context, + const std::optional& label, + const std::optional& fixed, + const std::optional& rlen, + const std::optional& llen, + bool noSeparator = false) + { + CryptoAlgorithm derive; + derive.setParam(ParamName::ALGO_TYPE, AlgoType::KBKDF); + if (len) + derive.setParam(ParamName::KDF_LEN, *len); + if (prf) + derive.setParam(ParamName::KDF_PRF, *prf); + if (mode) + derive.setParam(ParamName::KBKDF_MODE, *mode); + if (location) + derive.setParam(ParamName::KBKDF_COUNTER_LOCATION, *location); + if (context) + derive.setParam(ParamName::KBKDF_CONTEXT, *context); + if (label) + derive.setParam(ParamName::KBKDF_LABEL, *label); + if (fixed) + derive.setParam(ParamName::KBKDF_FIXED_INPUT, *fixed); + if (rlen) + derive.setParam(ParamName::KBKDF_RLEN, *rlen); + if (llen) + derive.setParam(ParamName::KBKDF_LLEN, *llen); + if (noSeparator) + derive.setParam(ParamName::KBKDF_NO_SEPARATOR, 1); + + GObjUPtr key; + if (ok) { + Token derived; + BOOST_REQUIRE_NO_THROW(derived = secret->derive(derive, "", RawBuffer())); + + BOOST_REQUIRE(derived.backendId == CryptoBackend::OpenSSL); + BOOST_REQUIRE(derived.dataType == DataType::KEY_AES); + + BOOST_REQUIRE_NO_THROW(key = STORE.getObject(derived, "")); + + BOOST_REQUIRE(key->getBinary().size() == *len); + } else { + BOOST_REQUIRE_THROW(secret->derive(derive, "", RawBuffer()), Exc::Crypto::InputParam); + } + } + + GObjUPtr secret; +}; + } // namespace BOOST_AUTO_TEST_SUITE(SW_TEST) @@ -1112,4 +1228,115 @@ NEGATIVE_TEST_CASE(deriveECDH) BOOST_REQUIRE_THROW(rsaPrivate->derive(derive, "", RawBuffer()), Exc::Crypto::InputParam); } +POSITIVE_TEST_CASE(deriveKBKDFHMAC) +{ + KbkdfParamTester test; + + test.Ok(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + + test.Ok(16, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + test.Ok(24, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + + test.Ok(32, HMAC384, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + test.Ok(32, HMAC512, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + + test.Ok(32, HMAC256, COUNTER, AFTER, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + test.Ok(32, HMAC256, COUNTER, MIDDLE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + + test.Ok(32, HMAC256, COUNTER, BEFORE, ONE, LAB, NO_BUF, NO_SIZE, NO_SIZE); + test.Ok(32, HMAC256, COUNTER, BEFORE, EMPTY, LAB, NO_BUF, NO_SIZE, NO_SIZE); + test.Ok(32, HMAC256, COUNTER, BEFORE, CTX, ONE, NO_BUF, NO_SIZE, NO_SIZE); + test.Ok(32, HMAC256, COUNTER, BEFORE, CTX, EMPTY, NO_BUF, NO_SIZE, NO_SIZE); + test.Ok(32, HMAC256, COUNTER, BEFORE, EMPTY, EMPTY, NO_BUF, NO_SIZE, NO_SIZE); + + test.Ok(32, HMAC256, COUNTER, BEFORE, NO_BUF, NO_BUF, FIX, NO_SIZE, NO_SIZE); + test.Ok(32, HMAC256, COUNTER, AFTER, NO_BUF, NO_BUF, FIX, NO_SIZE, NO_SIZE); + + test.Ok(32, HMAC256, COUNTER, BEFORE, NO_BUF, NO_BUF, ONE, NO_SIZE, NO_SIZE); + test.Ok(32, HMAC256, COUNTER, BEFORE, NO_BUF, NO_BUF, EMPTY, NO_SIZE, NO_SIZE); + + test.Ok(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, 32, NO_SIZE); + test.Ok(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, 24, NO_SIZE); + test.Ok(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, 16, NO_SIZE); + test.Ok(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, 8, NO_SIZE); + + test.Ok(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, 32); + test.Ok(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, 24); + test.Ok(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, 16); + test.Ok(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, 8); + test.Ok(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, 0); + + test.Ok(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE, true); + test.Ok(32, HMAC256, COUNTER, MIDDLE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE, true); +} + +NEGATIVE_TEST_CASE(deriveKBKDFHMACwrongAlgo) +{ + Token token; + BOOST_REQUIRE_NO_THROW(token = STORE.import(Data(DataType::BINARY_DATA, RawBuffer(16)), + "", + EncryptionParams(), + RawBuffer())); + + GObjUPtr secret; + BOOST_REQUIRE_NO_THROW(secret = STORE.getObject(token, "")); + + CryptoAlgorithm derive; + + // no algorithm + BOOST_REQUIRE_THROW(secret->derive(derive, "", RawBuffer()), Exc::Crypto::InputParam); + + // wrong algorithm + derive.setParam(ParamName::ALGO_TYPE, AlgoType::ECDH); + BOOST_REQUIRE_THROW(secret->derive(derive, "", RawBuffer()), Exc::Crypto::InputParam); +} + +NEGATIVE_TEST_CASE(deriveKBKDFHMACwrongParams) +{ + KbkdfParamTester test; + + // missing parameters + test.Fail(NO_SIZE, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + test.Fail(32, NO_PRF, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + test.Fail(32, HMAC256, NO_MODE, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + test.Fail(32, HMAC256, COUNTER, NO_LOC, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + test.Fail(32, HMAC256, COUNTER, BEFORE, NO_BUF, LAB, NO_BUF, NO_SIZE, NO_SIZE); + test.Fail(32, HMAC256, COUNTER, BEFORE, CTX, NO_BUF, NO_BUF, NO_SIZE, NO_SIZE); + + // conflicting parameters + test.Fail(32, HMAC256, COUNTER, BEFORE, CTX, LAB, FIX, NO_SIZE, NO_SIZE); + test.Fail(32, HMAC256, COUNTER, BEFORE, NO_BUF, LAB, FIX, NO_SIZE, NO_SIZE); + test.Fail(32, HMAC256, COUNTER, BEFORE, CTX, NO_BUF, FIX, NO_SIZE, NO_SIZE); + test.Fail(32, HMAC256, COUNTER, MIDDLE, NO_BUF, NO_BUF, FIX, NO_SIZE, NO_SIZE); + test.Fail(32, HMAC256, COUNTER, MIDDLE, NO_BUF, NO_BUF, FIX, NO_SIZE, 32); + test.Fail(32, HMAC256, COUNTER, MIDDLE, NO_BUF, NO_BUF, FIX, NO_SIZE, 0); + test.Fail(32, HMAC256, COUNTER, MIDDLE, NO_BUF, NO_BUF, FIX, NO_SIZE, NO_SIZE, true); + + // invalid values + test.Fail(0, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + test.Fail(1, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + test.Fail(8, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + test.Fail(64, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + + test.Fail(32, static_cast(0), COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + test.Fail(32, static_cast(4), COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + + test.Fail(32, HMAC256, static_cast(0), BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + test.Fail(32, HMAC256, static_cast(2), BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + + auto wrongLocation1 = static_cast(0); + auto wrongLocation2 = static_cast(4); + test.Fail(32, HMAC256, COUNTER, wrongLocation1, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + test.Fail(32, HMAC256, COUNTER, wrongLocation2, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE); + + test.Fail(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, 0, NO_SIZE); + test.Fail(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, 1, NO_SIZE); + test.Fail(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, 7, NO_SIZE); + test.Fail(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, 64, NO_SIZE); + + test.Fail(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, 1); + test.Fail(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, 7); + test.Fail(32, HMAC256, COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, 64); +} + BOOST_AUTO_TEST_SUITE_END()