From: Krzysztof Jackiewicz Date: Tue, 16 May 2023 13:35:30 +0000 (+0200) Subject: Fix backend selection logic X-Git-Tag: accepted/tizen/6.0/unified/20230621.004733~1^2~20 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F87%2F292887%2F2;p=platform%2Fcore%2Fsecurity%2Fkey-manager.git Fix backend selection logic 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 --- diff --git a/src/manager/common/data-type.h b/src/manager/common/data-type.h index 33884b7..017e214 100644 --- a/src/manager/common/data-type.h +++ b/src/manager/common/data-type.h @@ -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. */ diff --git a/src/manager/crypto/platform/decider.cpp b/src/manager/crypto/platform/decider.cpp index 9109fb2..8dca91c 100644 --- a/src/manager/crypto/platform/decider.cpp +++ b/src/manager/crypto/platform/decider.cpp @@ -34,43 +34,6 @@ 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 -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(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 Decider::getCompatibleBackends(DataType data, + const Policy &policy, + bool import, + bool encrypted) +{ + std::deque 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 diff --git a/src/manager/crypto/platform/decider.h b/src/manager/crypto/platform/decider.h index 08ab78e..3315e76 100644 --- a/src/manager/crypto/platform/decider.h +++ b/src/manager/crypto/platform/decider.h @@ -20,6 +20,8 @@ */ #pragma once +#include + #include #include @@ -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 getCompatibleBackends(DataType data, + const Policy &policy, + bool import = true, + bool encrypted = false); SW::Store m_swStore; #ifdef TZ_BACKEND_ENABLED diff --git a/src/manager/service/ckm-logic.cpp b/src/manager/service/ckm-logic.cpp index 21f0ac3..a5b158e 100644 --- a/src/manager/service/ckm-logic.cpp +++ b/src/manager/service/ckm-logic.cpp @@ -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(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(wrappingKey->backendId()) << " with given policy"); + return CKM_API_ERROR_INPUT_PARAM; + } + Token token = wrappingKey->unwrap(params, Crypto::Data(keyType, std::move(wrappedKey)), policy.password,