Fix backend selection logic 87/292887/2
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Tue, 16 May 2023 13:35:30 +0000 (15:35 +0200)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Thu, 18 May 2023 12:54:31 +0000 (14:54 +0200)
In some cases the backend selection was not working properly:
- Key derivation, wrapped key import: the backend compatibility was not
  checked at all. This resulted in a possibility of saving an exportable
  key in TZ backend which normally is not allowed.
- Encrypted initial values could have been imported to incompatible SW
  backend if the TZ backend fails to initialize or the SW backend is
  forced.

The Decider API was also unclear and different policies were in force
depending on the usecase.

This commit introduces following changes:
* Keep the policy in a single place.
* Return a prioritized list of backends compatible with given use case.
* Add backend check to key derivation and wrapped key import.
* Do not assume SW backend is suitable for all cases.
* Handle illegal cases by returning empty list of compatible backends.

Change-Id: I2d5dbbb3c4ba9385ac756eb419f95ac877cdd532

src/manager/common/data-type.h
src/manager/crypto/platform/decider.cpp
src/manager/crypto/platform/decider.h
src/manager/service/ckm-logic.cpp

index 33884b753f5cdae92054357b471495d906840e2d..017e21441c8f6b9a3d37fb2faef3632f9063820d 100644 (file)
@@ -81,7 +81,7 @@ public:
        bool isKey() const;
 
        /*
-        * Number of times someone mistook it for isKey() (or the opposite): 3
+        * Number of times someone mistook it for isKey() (or the opposite): 4
         * Increase the counter if it happened to you.
         * I will rename this function if the counter reaches 4.
         */
index 9109fb2e915790f376de0d17ca72539ecb18d30e..8dca91cb8acb51ed7e56ae680bd0788015b59507 100644 (file)
 namespace CKM {
 namespace Crypto {
 
-namespace {
-
-#ifdef TZ_BACKEND_ENABLED
-CryptoBackend tryGetTzBackend()
-{
-       try {
-               LogDebug("Trying to open TA session...");
-               TZ::Internals::TrustZoneContext::Instance();
-       } catch (const Exc::Crypto::InternalError& e) {
-               LogDebug("...failed. Selecting SW backend.");
-               return CryptoBackend::OpenSSL;
-       }
-
-       LogDebug("...succeeded. Selecting TZ backend.");
-       return CryptoBackend::TrustZone;
-}
-#endif
-
-template <class ForceOpenSSL>
-CryptoBackend chooseBackend(const Policy &policy, const ForceOpenSSL &forceOpenSSL)
-{
-#ifdef TZ_BACKEND_ENABLED
-       switch (policy.backend) {
-               case CKM::PolicyBackend::FORCE_SOFTWARE: return CryptoBackend::OpenSSL;
-               case CKM::PolicyBackend::FORCE_HARDWARE: return CryptoBackend::TrustZone;
-               case CKM::PolicyBackend::DEFAULT: break;
-       }
-       return forceOpenSSL() ? CryptoBackend::OpenSSL : tryGetTzBackend();
-#else // TZ_BACKEND_ENABLED
-       (void)policy;
-       (void)forceOpenSSL;
-       return CryptoBackend::OpenSSL;
-#endif // TZ_BACKEND_ENABLED
-}
-
-} // namespace
-
 Decider::Decider()
        : m_swStore(CryptoBackend::OpenSSL)
 #ifdef TZ_BACKEND_ENABLED
@@ -80,44 +43,126 @@ Decider::Decider()
 }
 
 GStore &Decider::getStore(const Token &token)
-{
-       return getStore(token.backendId);
-};
-
-GStore &Decider::getStore(CryptoBackend cryptoBackend)
 {
        GStore *gStore = NULL;
 
-       if (cryptoBackend == CryptoBackend::OpenSSL)
+       if (token.backendId == CryptoBackend::OpenSSL)
                gStore = &m_swStore;
 #ifdef TZ_BACKEND_ENABLED
-       if (cryptoBackend == CryptoBackend::TrustZone)
+       if (token.backendId == CryptoBackend::TrustZone)
                gStore = &m_tzStore;
 #endif
        if (gStore)
                return *gStore;
 
        ThrowErr(Exc::Crypto::InternalError,
-                        "Backend not available. BackendId: ", (int)cryptoBackend);
+                "Backend not available. BackendId: ",
+                static_cast<int>(token.backendId));
+};
+
+GStore* Decider::tryBackend(CryptoBackend backend)
+{
+       switch(backend) {
+       case CryptoBackend::OpenSSL:
+               return &m_swStore;
+       case CryptoBackend::TrustZone:
+#ifdef TZ_BACKEND_ENABLED
+               try {
+                       LogDebug("Trying to open TA session...");
+                       TZ::Internals::TrustZoneContext::Instance();
+                       LogDebug("...succeeded. Selecting TZ backend.");
+                       return &m_tzStore;
+               } catch (const Exc::Crypto::InternalError& e) {
+                       LogDebug("...failed.");
+               }
+#endif
+       default:
+               break;
+       }
+       return nullptr;
+}
+
+/*
+ * operation encrypted type   extractable backend
+ * ----------------------------------------------
+ * import    FALSE     binary -           TZ/SW
+ *                     skey   FALSE       TZ/SW
+ *                     skey   TRUE        SW
+ *                     akey   -           SW
+ *                     cert   -           SW
+ *           TRUE      binary -           TZ
+ *                     skey   FALSE       TZ
+ *                     skey   TRUE        NONE
+ *                     akey   -           NONE
+ *                     cert   -           NONE
+ * generate  -         binary FALSE       TZ/SW
+ *           -         binary TRUE        SW
+ *           -         cert   -           NONE
+ *           -         skey   FALSE       TZ/SW
+ *           -         skey   TRUE        SW
+ *           -         akey   FALSE       TZ/SW
+ *           -         akey   TRUE        SW
+ */
+std::deque<CryptoBackend> Decider::getCompatibleBackends(DataType data,
+                                                         const Policy &policy,
+                                                         bool import,
+                                                         bool encrypted)
+{
+       std::deque<CryptoBackend> backends;
+
+       auto addSW = [&]{
+               if (policy.backend != CKM::PolicyBackend::FORCE_HARDWARE)
+                       backends.push_back(CryptoBackend::OpenSSL);
+       };
+
+       auto addTZ = [&]{
+#ifdef TZ_BACKEND_ENABLED
+               if (policy.backend != CKM::PolicyBackend::FORCE_SOFTWARE)
+                       backends.push_front(CryptoBackend::TrustZone);
+#endif
+       };
+
+       if (import) {
+               if (!encrypted)
+                       addSW();
+
+               if (data.isBinaryData() || (data.isSKey() && !policy.extractable))
+                       addTZ();
+       } else { // generate/derive
+               assert(!encrypted);
+
+               if (!data.isCertificate() && !data.isChainCert()) {
+                       addSW();
+
+                       if (!policy.extractable)
+                               addTZ();
+               }
+       }
+       return backends;
 }
 
-GStore &Decider::getStore(DataType data, const Policy &policy, bool encrypted)
+GStore &Decider::getStore(DataType data, const Policy &policy, bool import, bool encrypted)
 {
-       return getStore(chooseBackend(policy, [&]{
-               return !encrypted && !data.isBinaryData() && (
-                       // tz-backend allows only for data binary export
-                       policy.extractable ||
-                       // Use TrustZone only with symmetric keys or unencrypted binary
-                       // data until asymmetric cryptography is implemented
-                       !data.isSKey()
-               );
-       }));
+       auto backends = getCompatibleBackends(data, policy, import, encrypted);
+       if (backends.empty())
+               ThrowErr(Exc::Crypto::InputParam, "No backend supports this operation.");
+
+       for (auto id : backends) {
+               auto backend = tryBackend(id);
+               if (backend != nullptr)
+                       return *backend;
+       }
+       ThrowErr(Exc::Crypto::InternalError, "Failed to connect to a compatible backend.");
 }
 
-GStore &Decider::getStore(const Policy &policyPrv)
+bool Decider::checkStore(CryptoBackend requestedBackend, DataType data, const Policy &policy, bool import)
 {
-       // extractable private key can only be handled by OpenSSL
-       return getStore(chooseBackend(policyPrv, [&]{ return policyPrv.extractable; }));
+       auto backends = getCompatibleBackends(data, policy, import);
+       for (auto id : backends) {
+               if (id == requestedBackend)
+                       return true;
+       }
+       return false;
 }
 
 } // namespace Crypto
index 08ab78eefda16873eab6b622f1b85feb6febd4ee..3315e764b049f3c704030e3ba62ed832d24168a3 100644 (file)
@@ -20,6 +20,8 @@
  */
 #pragma once
 
+#include <deque>
+
 #include <ckm/ckm-type.h>
 
 #include <crypto-backend.h>
@@ -40,11 +42,18 @@ class Decider final {
 public:
        Decider();
        GStore &getStore(const Token &token);
-       GStore &getStore(DataType data, const Policy &policy, bool encrypted = false);
-       GStore &getStore(const Policy &policyPrv);
+       GStore &getStore(DataType data,
+                        const Policy &policy,
+                        bool import = true,
+                        bool encrypted = false);
+       bool checkStore(CryptoBackend id, DataType data, const Policy &policy, bool import);
 
 private:
-       GStore &getStore(CryptoBackend id);
+       GStore* tryBackend(CryptoBackend backend);
+       std::deque<CryptoBackend> getCompatibleBackends(DataType data,
+                                                       const Policy &policy,
+                                                       bool import = true,
+                                                       bool encrypted = false);
 
        SW::Store m_swStore;
 #ifdef TZ_BACKEND_ENABLED
index 21f0ac31f026bf72f45a66a59654d23430e3a26e..a5b158ef8e6bef42d538d704c9060f04b82cc7ce 100644 (file)
@@ -1028,7 +1028,7 @@ int CKMLogic::importInitialData(
                        if (retCode != CKM_API_SUCCESS)
                                return retCode;
 
-                       Crypto::GStore &store = m_decider.getStore(data.type, policy, !encParams.iv.empty());
+                       Crypto::GStore &store = m_decider.getStore(data.type, policy, true, !encParams.iv.empty());
 
                        Token token;
                        if (encParams.iv.empty()) {
@@ -1177,7 +1177,8 @@ RawBuffer CKMLogic::createKeyPair(
                bool exportable = policyPrv.extractable || policyPub.extractable;
                Policy lessRestricted(Password(), exportable, policyPrv.backend);
 
-               TokenPair keys = m_decider.getStore(policyPrv).generateAKey(
+               // For now any asymmetric key will do. If necessary we can extract it from keyGenParams.
+               TokenPair keys = m_decider.getStore(DataType::DB_KEY_FIRST, policyPrv, false).generateAKey(
                        keyGenParams,
                        policyPrv.password,
                        policyPub.password,
@@ -1210,9 +1211,9 @@ RawBuffer CKMLogic::createKeyAES(
                        CryptoAlgorithm keyGenAlgorithm;
                        keyGenAlgorithm.setParam(ParamName::ALGO_TYPE, AlgoType::AES_GEN);
                        keyGenAlgorithm.setParam(ParamName::GEN_KEY_LEN, size);
-                       Token key = m_decider.getStore(DataType::KEY_AES, policy).generateSKey(keyGenAlgorithm,
-                                                                                                                                                                  policy.password,
-                                                                                                                                                                  digest);
+
+                       auto& store = m_decider.getStore(DataType::KEY_AES, policy, false);
+                       Token key = store.generateSKey(keyGenAlgorithm, policy.password, digest);
 
                        dbOp.finalize(std::move(key), policy);
                        return CKM_API_SUCCESS;
@@ -1537,14 +1538,15 @@ RawBuffer CKMLogic::deriveKey(
        return SerializeMessage(msgID, tryRet([&] {
                // Get key/secret for internal service use. It won't be exported to the client
                Crypto::GObjUPtr obj;
+               DataType objType;
                int retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST,
-                                                                        secretName, secretOwner, secretPassword, obj);
+                                                                        secretName, secretOwner, secretPassword, obj, objType);
                if (retCode != CKM_API_SUCCESS) {
                        if (retCode != CKM_API_ERROR_DB_ALIAS_UNKNOWN)
                                return retCode;
 
                        retCode = readDataHelper(false, cred, DataType::BINARY_DATA,
-                                                                        secretName, secretOwner, secretPassword, obj);
+                                                                        secretName, secretOwner, secretPassword, obj, objType);
                        if (retCode != CKM_API_SUCCESS)
                                return retCode;
                }
@@ -1553,6 +1555,14 @@ RawBuffer CKMLogic::deriveKey(
                if (ret != CKM_API_SUCCESS)
                        return ret;
 
+               // ECDH (private key) -> binary secret, KBKDF -> symmetric key
+               DataType newKeyType = objType.isKeyPrivate() ? DataType::BINARY_DATA : DataType::KEY_AES;
+               if (!m_decider.checkStore(obj->backendId(), newKeyType, newKeyPolicy, false)) {
+                       LogDebug("Can't import the derived key to backend " <<
+                                static_cast<int>(obj->backendId()) << " with given policy");
+                       return CKM_API_ERROR_INPUT_PARAM;
+               }
+
                // derive
                Token derived = obj->derive(params, newKeyPolicy.password, digest);
 
@@ -1587,6 +1597,12 @@ RawBuffer CKMLogic::importWrappedKey(
                if (retCode != CKM_API_SUCCESS)
                        return retCode;
 
+               if (!m_decider.checkStore(wrappingKey->backendId(), keyType, policy, true)) {
+                       LogDebug("Can't import the wrapped key to backend " <<
+                                static_cast<int>(wrappingKey->backendId()) << " with given policy");
+                       return CKM_API_ERROR_INPUT_PARAM;
+               }
+
                Token token = wrappingKey->unwrap(params,
                                                                                  Crypto::Data(keyType, std::move(wrappedKey)),
                                                                                  policy.password,