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 33884b7..017e214 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 9109fb2..8dca91c 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
@@ -81,43 +44,125 @@ 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 08ab78e..3315e76 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 21f0ac3..a5b158e 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,