KBKDF HMAC implementation in sw backend 67/288367/12
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Mon, 13 Feb 2023 09:10:39 +0000 (10:10 +0100)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Fri, 17 Mar 2023 12:23:40 +0000 (13:23 +0100)
Unit tests included

Change-Id: If4287b38c61fe3842024c5e7baf4934685f92566

src/manager/crypto/sw-backend/internals.cpp
src/manager/crypto/sw-backend/internals.h
src/manager/crypto/sw-backend/obj.cpp
src/manager/crypto/sw-backend/obj.h
unit-tests/test_sw-backend.cpp

index e1e40a5..9796b2e 100644 (file)
@@ -34,6 +34,7 @@
 #include <openssl/err.h>
 #include <openssl/x509v3.h>
 #include <openssl/obj_mac.h>
+#include <openssl/kdf.h>
 
 #include <ckm/ckm-error.h>
 #include <key-impl.h>
@@ -45,8 +46,8 @@
 #include <generic-backend/crypto-params.h>
 #include <sw-backend/internals.h>
 #include <sw-backend/crypto.h>
-
 #include <openssl-error-handler.h>
+#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<ParamName::ECDH_PUBKEY,
                true,
                DefaultValidator<RawBuffer>> EcdhPubKeyCheck;
 
+typedef ParamCheck<ParamName::ALGO_TYPE,
+               AlgoType,
+               true,
+               Type<AlgoType>::Equals<AlgoType::KBKDF>> IsKbkdf;
+
+typedef ParamCheck<ParamName::KDF_PRF,
+               KdfPrf,
+               true,
+               Type<KdfPrf>::Equals<KdfPrf::HMAC_SHA256,
+                                                        KdfPrf::HMAC_SHA384,
+                                                        KdfPrf::HMAC_SHA512>> KdfPrfCheck;
+
+typedef ParamCheck<ParamName::KBKDF_MODE,
+               KbkdfMode,
+               true,
+               Type<KbkdfMode>::Equals<KbkdfMode::COUNTER>> KbkdfModeCheck;
+
+typedef ParamCheck<ParamName::KDF_LEN,
+               int,
+               true,
+               Type<int>::Equals<16, 24, 32>> KdfLenCheck;
+
+typedef ParamCheck<ParamName::KBKDF_COUNTER_LOCATION,
+               KbkdfCounterLocation,
+               true,
+               Type<KbkdfCounterLocation>::Equals<KbkdfCounterLocation::BEFORE_FIXED,
+                                                                                  KbkdfCounterLocation::AFTER_FIXED,
+                                                                                  KbkdfCounterLocation::MIDDLE_FIXED>> KbkdfCounterLocationCheck;
+
+typedef ParamCheck<ParamName::KBKDF_RLEN,
+               int,
+               false,
+               Type<int>::Equals<8, 16, 24, 32>> KbkdfRlenCheck;
+
+typedef ParamCheck<ParamName::KBKDF_LLEN,
+               int,
+               false,
+               Type<int>::Equals<0, 8, 16, 24, 32>> KbkdfLlenCheck;
+
+
 typedef std::map<AlgoType, ValidatorVector> ValidatorMap;
 ValidatorMap initValidators()
 {
@@ -214,6 +255,12 @@ ValidatorMap initValidators()
        validators.emplace(AlgoType::AES_GCM, VBuilder<GcmIvCheck, GcmTagCheck>::Build());
        validators.emplace(AlgoType::RSA_OAEP, VBuilder<RsaLabelCheck>::Build());
        validators.emplace(AlgoType::ECDH, VBuilder<EcdhPubKeyCheck>::Build());
+       validators.emplace(AlgoType::KBKDF, VBuilder<KdfPrfCheck,
+                                                                                                KbkdfModeCheck,
+                                                                                                KdfLenCheck,
+                                                                                                KbkdfCounterLocationCheck,
+                                                                                                KbkdfRlenCheck,
+                                                                                                KbkdfLlenCheck>::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<IsKbkdf>(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
index 52f8a2e..b2669a6 100644 (file)
@@ -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
index f256eea..9b4e1a2 100644 (file)
@@ -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
index ffb8231..2712341 100644 (file)
@@ -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;
index f21e679..4de683e 100644 (file)
@@ -18,6 +18,7 @@
 #include <utility>
 #include <memory>
 #include <exception>
+#include <optional>
 
 #include <boost_macros_wrapper.h>
 #include <test_common.h>
@@ -228,6 +229,121 @@ struct CertHelper : public Cert {
        using Cert::getEvpShPtr;
 };
 
+const std::optional<RawBuffer> NO_BUF;
+const std::optional<size_t> NO_SIZE;
+const std::optional<KdfPrf> NO_PRF;
+const std::optional<KbkdfMode> NO_MODE;
+const std::optional<KbkdfCounterLocation> 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<size_t>& len,
+                       const std::optional<KdfPrf>& prf,
+                       const std::optional<KbkdfMode>& mode,
+                       const std::optional<KbkdfCounterLocation>& location,
+                       const std::optional<RawBuffer>& context,
+                       const std::optional<RawBuffer>& label,
+                       const std::optional<RawBuffer>& fixed,
+                       const std::optional<size_t>& rlen,
+                       const std::optional<size_t>& llen,
+                       bool noSeparator = false)
+       {
+               return Test(true, len, prf, mode, location, context, label, fixed, rlen, llen, noSeparator);
+       }
+
+       void Fail(const std::optional<size_t>& len,
+                         const std::optional<KdfPrf>& prf,
+                         const std::optional<KbkdfMode>& mode,
+                         const std::optional<KbkdfCounterLocation>& location,
+                         const std::optional<RawBuffer>& context,
+                         const std::optional<RawBuffer>& label,
+                         const std::optional<RawBuffer>& fixed,
+                         const std::optional<size_t>& rlen,
+                         const std::optional<size_t>& 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<size_t>& len,
+                         const std::optional<KdfPrf>& prf,
+                         const std::optional<KbkdfMode>& mode,
+                         const std::optional<KbkdfCounterLocation>& location,
+                         const std::optional<RawBuffer>& context,
+                         const std::optional<RawBuffer>& label,
+                         const std::optional<RawBuffer>& fixed,
+                         const std::optional<size_t>& rlen,
+                         const std::optional<size_t>& 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<KdfPrf>(0), COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE);
+       test.Fail(32, static_cast<KdfPrf>(4), COUNTER, BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE);
+
+       test.Fail(32, HMAC256, static_cast<KbkdfMode>(0), BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE);
+       test.Fail(32, HMAC256, static_cast<KbkdfMode>(2), BEFORE, CTX, LAB, NO_BUF, NO_SIZE, NO_SIZE);
+
+       auto wrongLocation1 = static_cast<KbkdfCounterLocation>(0);
+       auto wrongLocation2 = static_cast<KbkdfCounterLocation>(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()