Implemented Update API for key, certificate, pkcs12, data. 74/315474/68
authorAndrei Vakulich <a.vakulich@samsung.com>
Thu, 18 Jul 2024 12:34:15 +0000 (14:34 +0200)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Tue, 12 Nov 2024 09:34:33 +0000 (09:34 +0000)
Update API includes following functions:
ckmc_update_key, ckmc_update_cert,
ckmc_update_pkcs12, ckmc_update_data

Change-Id: Ib1809a9d86b9744465240a2e730fe047d334cf9e

14 files changed:
src/include/ckm/ckm-type.h
src/include/ckmc/ckmc-manager.h
src/manager/client-capi/ckmc-manager.cpp
src/manager/common/protocols.cpp
src/manager/common/token.h
src/manager/crypto/generic-backend/gstore.h
src/manager/dpl/db/include/dpl/db/sql_connection.h
src/manager/service/ckm-logic.cpp
src/manager/service/ckm-logic.h
src/manager/service/db-crypto.cpp
src/manager/service/db-crypto.h
unit-tests/CMakeLists.txt
unit-tests/test_ckm_logic.cpp [new file with mode: 0644]
unit-tests/timer.h [new file with mode: 0644]

index 60c681a11561e88d7aadc8a628c451a4eeb5e5e3..f6c51bc3936ed8955f5bcf7a8a651c62bedde314 100644 (file)
@@ -126,15 +126,18 @@ enum class PolicyBackend : int {
 };
 
 struct Policy {
-       Policy(const Password &pass = Password(), bool extract = true, PolicyBackend policyBackend = PolicyBackend::DEFAULT)
+       Policy(const Password &pass = Password(), bool extract = true,
+                  PolicyBackend policyBackend = PolicyBackend::DEFAULT, const bool overwrite = false)
          : password(pass)
          , extractable(extract)
          , backend(policyBackend)
+         , overwritable(overwrite)
        {}
        virtual ~Policy() {}
        Password password;  // byte array used to encrypt data inside CKM
        bool extractable;   // if true key may be extracted from storage
        PolicyBackend backend;
+       bool overwritable;  // if true data may be overwritten
 };
 
 enum class HashAlgorithm : int {
index 92334a13c8370dcfc0e4b4a730f7a5ca76ffa732..860975b8f79f49c207aa89b336fb93dea0ba301e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000 - 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2000 - 2024 Samsung Electronics Co., Ltd All Rights Reserved
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -87,6 +87,58 @@ int ckmc_save_key(const char *alias,
                   const ckmc_policy_s policy);
 
 
+/**
+ * @brief Updates the key inside the key manager based on the provided policy.
+ *
+ * @since_tizen 8.0
+ *
+ * @remarks Currently API supports nine types of keys. These are RSA public/private key,
+ *          DSA public/private key, ECDSA public/private key, KEM public/private key and
+ *          AES symmetric key.
+ * @remarks key_type in the key manager may be set to #CKMC_KEY_NONE as an input. key_type is
+ *          determined inside the key manager during updating key.
+ * @remarks Some private key files are protected with the password. If raw_key in the key is
+ *          encrypted with the password, the password should be provided in the #ckmc_key_s
+ *          structure.
+ * @remarks If password exists in the provided policy, the key is additionally encrypted with
+ *          the password from the provided policy.
+ * @remarks If password changes in the provided policy, the key is re-encrypted with the new
+ *          password inside the key manager.
+ * @remarks If nothing exists inside the key manager under the provided alias, the key is saved
+ *          inside the key manager based on the provided policy.
+ * @remarks If anything exists inside the key manager under the provided alias, the old data is
+ *          replaced with the key inside the key manager based on the provided policy.
+ * @remarks If extractable flag changes in the provided policy, the extractable flag is updated
+ *          inside the key manager.
+ * @remarks If backend changes, the key is removed from the old backend and saved in the new
+ *          backend.
+ * @remarks Existing permissions are reset inside the key manager.
+ *
+ * @param[in] alias The name of the key to be updated
+ * @param[in] key The key's binary value to be updated
+ * @param[in] policy The policy about how to update the key securely
+ *
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #CKMC_ERROR_NONE Successful
+ * @retval #CKMC_ERROR_PERMISSION_DENIED Failed to access the key manager
+ * @retval #CKMC_ERROR_INVALID_PARAMETER Input parameter is invalid
+ * @retval #CKMC_ERROR_DB_LOCKED The user key is not loaded in memory (a user is not logged in)
+ * @retval #CKMC_ERROR_INVALID_FORMAT The format of raw_key is not valid
+ * @retval #CKMC_ERROR_DB_ERROR Failed due to a database error
+ *
+ * @pre User is already logged in and the user key is already loaded into memory in plain text
+ *      form.
+ *
+ * @see ckmc_save_key()
+ * @see ckmc_remove_alias()
+ * @see ckmc_get_key()
+ * @see ckmc_get_key_alias_list()
+ * @see #ckmc_key_s
+ * @see #ckmc_policy_s
+ */
+int ckmc_update_key(const char *alias, const ckmc_key_s key, const ckmc_policy_s policy);
+
+
 /**
  * @deprecated Deprecated since 2.4
  *             [Use ckmc_remove_alias() instead]
@@ -255,6 +307,53 @@ int ckmc_save_cert(const char *alias,
                    const ckmc_policy_s policy);
 
 
+/**
+ * @brief Updates the certificate inside the key manager based on the provided policy.
+ *
+ * @since_tizen 8.0
+ *
+ * @remarks The certificate's binary value will be converted and updated as binary DER encoded
+ *          certificate.
+ * @remarks If password exists in the provided policy, the certificate is additionally encrypted
+ *          with the password from the provided policy.
+ * @remarks If password changes in the provided policy, the certificate is re-encrypted with the
+ *          new password inside the key manager.
+ * @remarks If nothing exists inside the key manager under the provided alias, the certificate is
+ *          saved inside the key manager based on the provided policy.
+ * @remarks If anything exists inside the key manager under the provided alias, the old data is
+ *          replaced with the certificate inside the key manager based on the provided policy.
+ * @remarks If extractable flag changes in the provided policy, the extractable flag is updated
+ *          inside the key manager.
+ * @remarks If backend changes, the certificate is removed from the old backend and saved in the
+ *          new backend.
+ * @remarks Existing permissions are reset inside the key manager.
+ *
+ * @param[in] alias The name of the certificate to be updated
+ * @param[in] cert The certificate's binary value to be stored
+ * @param[in] policy The policy about how to update the certificate securely
+ *
+ * @return @c 0 on success, otherwise a negative error value
+ *
+ * @retval #CKMC_ERROR_NONE Successful
+ * @retval #CKMC_ERROR_PERMISSION_DENIED Failed to access key manager
+ * @retval #CKMC_ERROR_INVALID_PARAMETER Input parameter is invalid
+ * @retval #CKMC_ERROR_DB_LOCKED A user key is not loaded in memory (a user is not logged in)
+ * @retval #CKMC_ERROR_INVALID_FORMAT The format of raw_cert is not valid
+ * @retval #CKMC_ERROR_DB_ERROR Failed due to a database error
+ *
+ * @pre User is already logged in and the user key is already loaded into memory in plain text
+ *      form.
+ *
+ * @see ckmc_save_cert()
+ * @see ckmc_remove_alias()
+ * @see ckmc_get_cert()
+ * @see ckmc_get_cert_alias_list()
+ * @see #ckmc_cert_s
+ * @see #ckmc_policy_s
+ */
+int ckmc_update_cert(const char *alias, const ckmc_cert_s cert, const ckmc_policy_s policy);
+
+
 /**
  * @deprecated Deprecated since 2.4
  *             [Use ckmc_remove_alias() instead]
@@ -426,6 +525,58 @@ int ckmc_save_pkcs12(const char *alias,
                      const ckmc_policy_s cert_policy);
 
 
+/**
+ * @brief Updates PKCS12's contents inside the key manager based on the provided policies. All
+ *        items from the PKCS12 use the same alias.
+ *
+ * @since_tizen 8.0
+ *
+ * @remarks If password exists in the provided key/certificate policy, the key/certificate/chain
+ *          is additionally encrypted with the password from the provided key/certificate policy.
+ * @remarks If password changes in the provided key/certificate policy, the key/certificate/chain
+ *          is re-encrypted with the new password inside the key manager.
+ * @remarks If nothing exists inside the key manager under the provided alias, the pkcs12 is
+ *          saved inside the key manager based on the provided policy.
+ * @remarks If anything exists inside the key manager under the provided alias, the old data is
+ *          replaced with the pkcs12 inside the key manager based on the provided policy.
+ * @remarks If extractable flag changes in the provided policy, the extractable flag is updated
+ *          inside the key manager.
+ * @remarks If backend changes, the pkcs12 is removed from the old backend and saved in the new
+ *          backend.
+ *
+ * @remarks Existing permissions are reset inside the key manager.
+ *
+ * @param[in] alias The name of the pkcs12 to be updated
+ * @param[in] pkcs Pointer to the pkcs12 structure to be updated
+ * @param[in] key_policy The policy about how to store pkcs's private key
+ * @param[in] cert_policy The policy about how to store pkcs's certificate and certificate chain
+ *
+ * @return @c 0 on success, otherwise a negative error value
+ *
+ * @retval #CKMC_ERROR_NONE Successful
+ * @retval #CKMC_ERROR_PERMISSION_DENIED Failed to access key manager
+ * @retval #CKMC_ERROR_INVALID_PARAMETER Input parameter is invalid
+ * @retval #CKMC_ERROR_DB_LOCKED A user key is not loaded in memory (a user is not logged in)
+ * @retval #CKMC_ERROR_INVALID_FORMAT The format of raw_key or raw_cert is not valid
+ * @retval #CKMC_ERROR_DB_ERROR Failed due to a database error
+ *
+ * @pre User is already logged in and the user key is already loaded into memory in plain text
+ *      form.
+ *
+ * @see ckmc_save_pkcs12()
+ * @see ckmc_remove_alias()
+ * @see ckmc_get_pkcs12()
+ * @see ckmc_get_data_alias_list()
+ * @see ckmc_pkcs12_load()
+ * @see #ckmc_pkcs12_s
+ * @see #ckmc_policy_s
+ */
+int ckmc_update_pkcs12(const char *alias,
+                       const ckmc_pkcs12_s *pkcs,
+                       const ckmc_policy_s key_policy,
+                       const ckmc_policy_s cert_policy);
+
+
 /**
  * @brief Gets a pkcs12 from key manager.
  *
@@ -496,6 +647,50 @@ int ckmc_save_data(const char *alias,
                    const ckmc_policy_s policy);
 
 
+/**
+ * @brief Updates the data inside the key manager based on the provided policy.
+ *
+ * @since_tizen 8.0
+ *
+ * @remarks If password exists in the provided policy, the data is additionally encrypted with
+ *          the password from the provided policy.
+ * @remarks If password changes in the provided policy, the data is re-encrypted with the new
+ *          password inside the key manager.
+ * @remarks If nothing exists inside the key manager under the provided alias, the data is
+ *          saved inside the key manager based on the provided policy.
+ * @remarks If anything exists inside the key manager under the provided alias, the old data is
+ *          replaced with the new data inside the key manager based on the provided policy.
+ * @remarks If extractable flag changes in the provided policy, the extractable flag is updated
+ *          inside the key manager.
+ * @remarks If backend changes, the data is removed from the old backend and saved in the new
+ *          backend.
+ * @remarks Existing permissions are reset inside the key manager.
+ *
+ * @param[in] alias The name of the data to be updated
+ * @param[in] data The binary value to be stored
+ * @param[in] policy The policy about how to store the data securely
+ *
+ * @return @c 0 on success, otherwise a negative error value
+ *
+ * @retval #CKMC_ERROR_NONE Successful
+ * @retval #CKMC_ERROR_PERMISSION_DENIED Failed to access key manager
+ * @retval #CKMC_ERROR_INVALID_PARAMETER Input parameter is invalid
+ * @retval #CKMC_ERROR_DB_LOCKED A user key is not loaded in memory (a user is not logged in)
+ * @retval #CKMC_ERROR_DB_ERROR Failed due to a database error
+ *
+ * @pre User is already logged in and the user key is already loaded into memory in plain text
+ *      form.
+ *
+ * @see ckmc_save_data()
+ * @see ckmc_remove_alias()
+ * @see ckmc_get_data()
+ * @see ckmc_get_data_alias_list()
+ * @see #ckmc_raw_buffer_s
+ * @see #ckmc_policy_s
+ */
+int ckmc_update_data(const char *alias, ckmc_raw_buffer_s data, const ckmc_policy_s policy);
+
+
 /**
  * @deprecated Deprecated since 2.4
  *             [Use ckmc_remove_alias() instead]
index a0cb41b85d5c69539a18336c15befa3c58ea9b19..66a8e7d0bcbe1248394d72284d7dda078c0b4881 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2000-2020 Samsung Electronics Co., Ltd. All rights reserved
+ *  Copyright (c) 2000-2024 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -78,9 +78,10 @@ inline CKM::Password _tostring(const char *str)
        return (str == nullptr) ? CKM::Password() : CKM::Password(str);
 }
 
-inline CKM::Policy _toCkmPolicy(const ckmc_policy_s &policy)
+inline CKM::Policy _toCkmPolicy(const ckmc_policy_s &policy, const bool overwrite = false)
 {
-       return CKM::Policy(_tostring(policy.password), policy.extractable);
+       return CKM::Policy(_tostring(policy.password), policy.extractable,
+                                          CKM::PolicyBackend::DEFAULT, overwrite);
 }
 
 CKM::KeyShPtr _toCkmKey(const ckmc_key_s *key)
@@ -332,11 +333,7 @@ int get_alias_list(ckmc_alias_list_s **alias_list, int (CKM::Manager::*getAliasV
        EXCEPTION_GUARD_END
 }
 
-} // namespace anonymous
-
-KEY_MANAGER_CAPI
-int ckmc_save_key(const char *alias, const ckmc_key_s key,
-                                 const ckmc_policy_s policy)
+int save_key(const char *alias, const ckmc_key_s key, const CKM::Policy &policy)
 {
        EXCEPTION_GUARD_START_CAPI
 
@@ -364,12 +361,78 @@ int ckmc_save_key(const char *alias, const ckmc_key_s key,
        if (!ckmKey)
                return CKMC_ERROR_INVALID_FORMAT;
 
-       return to_ckmc_error(mgr->saveKey(CKM::Alias(alias), ckmKey,
-                                                                         _toCkmPolicy(policy)));
+       return to_ckmc_error(mgr->saveKey(CKM::Alias(alias), ckmKey, policy));
+
+       EXCEPTION_GUARD_END
+}
+
+int save_cert(const char *alias, const ckmc_cert_s cert, const CKM::Policy &policy)
+{
+       EXCEPTION_GUARD_START_CAPI
+
+       if (alias == nullptr || cert.raw_cert == nullptr || cert.cert_size == 0)
+               return CKMC_ERROR_INVALID_PARAMETER;
+
+       auto mgr = CKM::Manager::create();
+       return to_ckmc_error(mgr->saveCertificate(CKM::Alias(alias),
+                                                                                         _toCkmCertificate(&cert),
+                                                                                         policy));
+
+       EXCEPTION_GUARD_END
+}
+
+int save_pkcs12(const char *alias, const ckmc_pkcs12_s *ppkcs,
+                               const CKM::Policy &key_policy, const CKM::Policy &cert_policy)
+{
+       EXCEPTION_GUARD_START_CAPI
+
+       if (alias == nullptr || ppkcs == nullptr)
+               return CKMC_ERROR_INVALID_PARAMETER;
+
+       CKM::PKCS12ShPtr pkcs12(new CKM::PKCS12Impl(_toCkmKey(ppkcs->priv_key),
+                                                                                               _toCkmCertificate(ppkcs->cert),
+                                                                                               _toCkmCertificateVector(ppkcs->ca_chain)));
+
+       auto mgr = CKM::Manager::create();
+       return to_ckmc_error(mgr->savePKCS12(CKM::Alias(alias),
+                                                                                pkcs12,
+                                                                                key_policy,
+                                                                                cert_policy));
 
        EXCEPTION_GUARD_END
 }
 
+int save_data(const char *alias, ckmc_raw_buffer_s data,
+                         const CKM::Policy &policy)
+{
+       EXCEPTION_GUARD_START_CAPI
+
+       if (alias == nullptr || data.data == nullptr || data.size == 0)
+               return CKMC_ERROR_INVALID_PARAMETER;
+
+       auto mgr = CKM::Manager::create();
+       return to_ckmc_error(mgr->saveData(CKM::Alias(alias),
+                                                                          CKM::RawBuffer(data.data, data.data + data.size),
+                                                                          policy));
+
+       EXCEPTION_GUARD_END
+}
+
+} // namespace anonymous
+
+KEY_MANAGER_CAPI
+int ckmc_save_key(const char *alias, const ckmc_key_s key,
+                                 const ckmc_policy_s policy)
+{
+       return save_key(alias, key, _toCkmPolicy(policy));
+}
+
+KEY_MANAGER_CAPI
+int ckmc_update_key(const char *alias, const ckmc_key_s key,
+                                   const ckmc_policy_s policy)
+{
+       return save_key(alias, key, _toCkmPolicy(policy, true));
+}
 
 KEY_MANAGER_CAPI
 int ckmc_remove_key(const char *alias)
@@ -426,17 +489,14 @@ KEY_MANAGER_CAPI
 int ckmc_save_cert(const char *alias, const ckmc_cert_s cert,
                                   const ckmc_policy_s policy)
 {
-       EXCEPTION_GUARD_START_CAPI
-
-       if (alias == nullptr || cert.raw_cert == nullptr || cert.cert_size == 0)
-               return CKMC_ERROR_INVALID_PARAMETER;
-
-       auto mgr = CKM::Manager::create();
-       return to_ckmc_error(mgr->saveCertificate(CKM::Alias(alias),
-                                                _toCkmCertificate(&cert),
-                                                _toCkmPolicy(policy)));
+       return save_cert(alias, cert, _toCkmPolicy(policy));
+}
 
-       EXCEPTION_GUARD_END
+KEY_MANAGER_CAPI
+int ckmc_update_cert(const char *alias, const ckmc_cert_s cert,
+                                        const ckmc_policy_s policy)
+{
+       return save_cert(alias, cert, _toCkmPolicy(policy, true));
 }
 
 KEY_MANAGER_CAPI
@@ -490,24 +550,17 @@ KEY_MANAGER_CAPI
 int ckmc_save_pkcs12(const char *alias, const ckmc_pkcs12_s *ppkcs,
                                         const ckmc_policy_s key_policy, const ckmc_policy_s cert_policy)
 {
-       EXCEPTION_GUARD_START_CAPI
-
-       if (alias == nullptr || ppkcs == nullptr)
-               return CKMC_ERROR_INVALID_PARAMETER;
-
-       CKM::PKCS12ShPtr pkcs12(new CKM::PKCS12Impl(
-                                                               _toCkmKey(ppkcs->priv_key),
-                                                               _toCkmCertificate(ppkcs->cert),
-                                                               _toCkmCertificateVector(ppkcs->ca_chain)));
-
-       auto mgr = CKM::Manager::create();
-       return to_ckmc_error(mgr->savePKCS12(
-                                                        CKM::Alias(alias),
-                                                        pkcs12,
-                                                        _toCkmPolicy(key_policy),
-                                                        _toCkmPolicy(cert_policy)));
+       return save_pkcs12(alias, ppkcs, _toCkmPolicy(key_policy), _toCkmPolicy(cert_policy));
+}
 
-       EXCEPTION_GUARD_END
+KEY_MANAGER_CAPI
+int ckmc_update_pkcs12(const char *alias, const ckmc_pkcs12_s *ppkcs,
+                                          const ckmc_policy_s key_policy, const ckmc_policy_s cert_policy)
+{
+       return save_pkcs12(alias,
+                                          ppkcs,
+                                          _toCkmPolicy(key_policy, true),
+                                          _toCkmPolicy(cert_policy, true));
 }
 
 KEY_MANAGER_CAPI
@@ -586,18 +639,14 @@ KEY_MANAGER_CAPI
 int ckmc_save_data(const char *alias, ckmc_raw_buffer_s data,
                                   const ckmc_policy_s policy)
 {
-       EXCEPTION_GUARD_START_CAPI
-
-       if (alias == nullptr || data.data == nullptr || data.size == 0)
-               return CKMC_ERROR_INVALID_PARAMETER;
-
-       auto mgr = CKM::Manager::create();
-       return to_ckmc_error(mgr->saveData(
-                                                        CKM::Alias(alias),
-                                                        CKM::RawBuffer(data.data, data.data + data.size),
-                                                        _toCkmPolicy(policy)));
+       return save_data(alias, data, _toCkmPolicy(policy));
+}
 
-       EXCEPTION_GUARD_END
+KEY_MANAGER_CAPI
+int ckmc_update_data(const char *alias, ckmc_raw_buffer_s data,
+                                        const ckmc_policy_s policy)
+{
+       return save_data(alias, data, _toCkmPolicy(policy, true));
 }
 
 KEY_MANAGER_CAPI
index a4d8c7ca6b98a1ff3cf954b0e097b44b21c55313..13cbd615cb295c0adc014865e4516b17207e7082 100644 (file)
@@ -55,6 +55,7 @@ void PolicySerializable::Serialize(IStream &stream) const
        Serialization::Serialize(stream, password);
        Serialization::Serialize(stream, extractable);
        Serialization::Serialize(stream, backend);
+       Serialization::Serialize(stream, overwritable);
 }
 
 void PolicySerializable::Deserialize(IStream &stream)
@@ -62,6 +63,7 @@ void PolicySerializable::Deserialize(IStream &stream)
        Deserialization::Deserialize(stream, password);
        Deserialization::Deserialize(stream, extractable);
        Deserialization::Deserialize(stream, backend);
+       Deserialization::Deserialize(stream, overwritable);
 }
 
 PKCS12Serializable::PKCS12Serializable()
index 02022b303a548afd944a67225b6082218339d357..30997f2b34f25b7eb83b4d6de1ea13ce8387936d 100644 (file)
@@ -42,5 +42,6 @@ struct Token {
 };
 
 using TokenPair = std::pair<Token, Token>;
+using TokenVector = std::vector<Token>;
 
 } // namespace CKM
index 1348feaa2592d92067a8302ede57e3b8e1c0dbb8..380f99e1b0b3e1230c1604fcf94d5bc8f2682045 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2015-2021 Samsung Electronics Co., Ltd. All rights reserved
+ *  Copyright (c) 2015-2024 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -56,6 +56,8 @@ public:
        virtual void destroy(const Token &) = 0;
        virtual size_t maxChunkSize() const { return 0; }
 
+       CryptoBackend getBackendId() const { return m_backendId; }
+
        virtual ~GStore() {}
 
 protected:
index 25411b65007b6e1e18f1301aa30953636e9d554c..08df8ef67dc2cb351e895d759ca4a3e696a1d6b6 100644 (file)
@@ -38,6 +38,8 @@
 namespace CKM {
 namespace DB {
 
+class FailedSqlConnection;
+
 enum DBCommandID {
        DB_CMD_SCHEMA_SET_ID,
        DB_CMD_SCHEMA_GET_ID,
@@ -47,6 +49,8 @@ enum DBCommandID {
        DB_CMD_NAME_DELETE_BY_OWNER_ID,
        DB_CMD_OBJECT_INSERT_ID,
        DB_CMD_OBJECT_UPDATE_ID,
+       DB_CMD_OBJECT_UPDATE_OR_REPLACE_ID,
+       DB_CMD_OBJECT_DELETE_ID,
        DB_CMD_OBJECT_SELECT_BY_NAME_AND_OWNER_ID,
        DB_CMD_KEY_INSERT_ID,
        DB_CMD_KEY_SELECT_ID,
@@ -54,6 +58,7 @@ enum DBCommandID {
        DB_CMD_PERMISSION_SET_ID,
        DB_CMD_PERMISSION_SELECT_ID,
        DB_CMD_PERMISSION_DELETE_ID,
+       DB_CMD_PERMISSION_RESET_ID,
        DB_CMD_INFO_SELECT_BY_TYPE_AND_PERMISSION_ID,
        DB_CMD_SELECT_TABLE_ID,
        DB_CMD_OBJECT_SELECT_ID,
@@ -355,7 +360,7 @@ public:
         * Execute COMMIT; command to commit changes in database
         *
         */
-       void CommitTransaction();
+       virtual void CommitTransaction();
 
        /**
         * Prepare stored procedure
@@ -380,6 +385,8 @@ public:
 
 private:
        void ExecCommandHelper(Output *out, const char *format, va_list args);
+
+       friend FailedSqlConnection;
 };
 RawBuffer createHexPass(const RawBuffer &rawPass);
 } // namespace DB
index 08038daf48db51f28d58d19d03c844ef7f8bf06a..412f1e9b98b6aaa34c6db0027fb5eed57a5a27cc 100644 (file)
@@ -77,7 +77,7 @@ int tryRet(F &&f)
        }
 }
 
-int toBinaryData(const Crypto::Data &input, Crypto::Data &output)
+int toObject(const Crypto::Data &input, Crypto::Data &output)
 {
        // verify the data integrity
        if (input.type.isKey()) {
@@ -115,10 +115,10 @@ int toBinaryData(const Crypto::Data &input, Crypto::Data &output)
        return CKM_API_SUCCESS;
 }
 
-int verifyBinaryData(const Crypto::Data &input)
+int verifyObject(const Crypto::Data &input)
 {
        Crypto::Data dummy;
-       return toBinaryData(input, dummy);
+       return toObject(input, dummy);
 }
 
 int readSingleRow(const Name &name,
@@ -187,6 +187,22 @@ int readMultiRow(const Name &name,
        return CKM_API_SUCCESS;
 }
 
+void rollbackTransaction(DB::Crypto::Transaction& transaction)
+{
+       try
+       {
+               transaction.rollback();
+       }
+       catch (const DB::SqlConnection::Exception::InternalError &)
+       {
+               LogError("sqlite got into infinite busy state");
+       }
+       catch (const DB::SqlConnection::Exception::Base &)
+       {
+               LogError("Transaction rollback failed!");
+       }
+}
+
 } // namespace
 
 namespace Crypto {
@@ -270,7 +286,7 @@ void CKMLogic::migrateSecureStorageData(bool isAdminUser)
                auto ownerId = adminUserFlag ? CLIENT_ID_ADMIN_USER : CLIENT_ID_SYSTEM;
                auto uid = adminUserFlag ? ADMIN_USER_DB_UID : SYSTEM_DB_UID;
 
-               int ret = verifyAndSaveDataHelper(Credentials(uid, ownerId), name, ownerId, data,
+               int ret = verifyAndSaveObjectHelper(Credentials(uid, ownerId), name, ownerId, data,
                                                                                  PolicySerializable());
 
                if (ret == CKM_API_ERROR_DB_ALIAS_EXISTS)
@@ -466,50 +482,208 @@ RawBuffer CKMLogic::removeApplicationData(const ClientId &owner)
        }));
 }
 
-int CKMLogic::verifyAndSaveDataHelper(
+int CKMLogic::getKeyForService(
        const Credentials &cred,
        const Name &name,
        const ClientId &owner,
-       const Crypto::Data &data,
-       const PolicySerializable &policy)
+       const Password &pass,
+       Crypto::GObjShPtr &key)
 {
        return tryRet([&] {
-               // check if data is correct
-               Crypto::Data binaryData;
-               int retCode = toBinaryData(data, binaryData);
-               if (retCode != CKM_API_SUCCESS)
-                       return retCode;
+               // Key is for internal service use. It won't be exported to the client
+               Crypto::GObjUPtr obj;
+               int retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, name, owner, pass, obj);
+               if (retCode == CKM_API_SUCCESS)
+                       key = std::move(obj);
 
-               auto [dbOp, digest, ret] = beginSaveAndGetHash(cred, name, owner);
-               if (ret != CKM_API_SUCCESS)
-                       return ret;
+               return retCode;
+       });
+}
 
-               Crypto::GStore &store = m_decider.getStore(binaryData.type, policy);
+Token CKMLogic::importToStore(
+       const Crypto::Data &object,
+       const PolicySerializable &policy,
+       const RawBuffer &hash)
+{
+       // do not encrypt data with password during cc_mode on
+       return m_decider.getStore(object.type, policy).import(
+               object,
+               m_accessControl.isCCMode() ? Password() : policy.password,
+               Crypto::EncryptionParams(),
+               hash);
+}
 
-               // do not encrypt data with password during cc_mode on
-               Token token = store.import(binaryData,
-                                                                  m_accessControl.isCCMode() ? "" : policy.password,
-                                                                  Crypto::EncryptionParams(), digest);
-               dbOp.finalize(std::move(token), policy);
-               return CKM_API_SUCCESS;
-       });
+void CKMLogic::destroyInStore(
+       const Token &token)
+{
+       try {
+               m_decider.getStore(token).destroy(token);
+       } catch (const Exc::Exception& e) {
+               LogError("Could not destroy token from store. Ignored. " << e.what());
+       }
 }
 
-int CKMLogic::getKeyForService(
+void CKMLogic::destroyInStore(
+       const TokenVector &tokens)
+{
+       for (const auto& token : tokens)
+       {
+               destroyInStore(token);
+       }
+}
+
+void CKMLogic::decryptAndDestroyInStore(
+       CKM::UserData &handler,
+       DB::Row &row,
+       const RawBuffer &hash)
+{
+       try {
+               decryptRow(handler, row, Password(), hash);
+               m_decider.getStore(row).destroy(row);
+       } catch (const Exc::Exception& e) {
+               LogError("Could not destroy row from store. Ignored. " << e.what());
+       }
+}
+
+void CKMLogic::decryptAndDestroyInStore(
+       const Name &name,
+       const ClientId &owner,
        const Credentials &cred,
+       DB::RowVector &rows,
+       CKM::UserData &handler,
+       const size_t start)
+{
+       for (size_t i = start, size = rows.size(); i < size; ++i)
+       {
+               const auto oldHash = CryptoLogic::makeHash(name, owner, cred.clientUid,
+                                                                                                  rows[i].dataType);
+               if (oldHash.empty())
+               {
+                       LogError("Empty hash for name = " << name << ", owner = " << owner
+                                        << ", clientUid = " << cred.clientUid << ", dataType = "
+                                        << rows[i].dataType << ". Ignored.");
+                       continue;
+               }
+
+               decryptAndDestroyInStore(handler, rows[i], oldHash);
+       }
+}
+
+Token CKMLogic::updateInStore(
+       const Crypto::Data &newObject,
+       const PolicySerializable &newPolicy,
+       const RawBuffer &hash,
+       CKM::UserData &handler,
+       DB::Row &oldRow)
+{
+       Token token = importToStore(newObject, newPolicy, hash);
+
+       if (m_decider.getStore(token).getBackendId() != oldRow.backendId)
+       {
+               decryptAndDestroyInStore(handler, oldRow, hash);
+       }
+
+       return token;
+}
+
+void CKMLogic::importToStoreAndInsertInDb(
+       const Crypto::Data &object,
+       const PolicySerializable &policy,
+       const CKM::RawBuffer &hash,
+       DBOperation &dbOp)
+{
+       const auto token = importToStore(object, policy, hash);
+
+       try
+       {
+               dbOp.finalize(Token(token), policy);
+       }
+       catch(const Exc::Exception& e)
+       {
+               destroyInStore(token);
+               throw;
+       }
+}
+
+template <typename... Args>
+void CKMLogic::cleanupInStoreAndInDb(
+       DBOperation &dbOp,
        const Name &name,
        const ClientId &owner,
-       const Password &pass,
-       Crypto::GObjShPtr &key)
+       const Credentials &cred,
+       DB::RowVector oldPKCS12Rows,
+       const size_t oldPKCS12RowsStartIndex,
+       Args&&... tokens
+)
+{
+       rollbackTransaction(dbOp.transaction());
+       (destroyInStore(std::forward<Args>(tokens)), ...);
+       decryptAndDestroyInStore(name, owner, cred, oldPKCS12Rows,
+                                                        dbOp.handler(), oldPKCS12RowsStartIndex);
+       dbOp.database().deleteRow(name, owner);
+}
+
+int CKMLogic::verifyAndSaveObjectHelper(
+       const Credentials &cred,
+       const Name &name,
+       const ClientId &owner,
+       const Crypto::Data &data,
+       const PolicySerializable &policy)
 {
        return tryRet([&] {
-               // Key is for internal service use. It won't be exported to the client
-               Crypto::GObjUPtr obj;
-               int retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, name, owner, pass, obj);
-               if (retCode == CKM_API_SUCCESS)
-                       key = std::move(obj);
+               Crypto::Data newObject;
+               if (const auto retCode = toObject(data, newObject); retCode != CKM_API_SUCCESS)
+                       return retCode;
 
-               return retCode;
+               const auto newHash = CryptoLogic::makeHash(name, owner, cred.clientUid);
+               if (newHash.empty())
+                       return CKM_API_ERROR_HASH_ERROR;
+
+               auto [dbOp, retCode] = beginSave(cred, name, owner, policy.overwritable);
+               if (retCode != CKM_API_SUCCESS)
+                       return retCode;
+
+               if (!policy.overwritable)
+               {
+                       importToStoreAndInsertInDb(newObject, policy, newHash, dbOp);
+                       return CKM_API_SUCCESS;
+               }
+
+               DB::RowVector oldRows;
+               dbOp.database().getRows(name, owner, DataType::DB_FIRST, DataType::DB_LAST, oldRows);
+
+               if (oldRows.empty())
+               {
+                       importToStoreAndInsertInDb(newObject, policy, newHash, dbOp);
+                       return CKM_API_SUCCESS;
+               }
+
+               Token newToken{};
+               if (oldRows.size() == 1)
+               {
+                       newToken = updateInStore(newObject, policy, newHash,
+                                                                        dbOp.handler(), oldRows[0]);
+               }
+               else
+               {
+                       newToken = importToStore(newObject, policy, newHash);
+                       decryptAndDestroyInStore(name, owner, cred, oldRows, dbOp.handler());
+               }
+
+               try
+               {
+                       const auto newRow = dbOp.encryptOne(Token(newToken), policy);
+                       dbOp.database().updateOrReplaceRow(newRow);
+                       dbOp.database().resetPermissions(name, owner);
+                       dbOp.transaction().commit();
+               }
+               catch(const Exc::Exception& e)
+               {
+                       cleanupInStoreAndInDb(dbOp, name, owner, cred, {}, 0, newToken);
+                       throw;
+               }
+
+               return CKM_API_SUCCESS;
        });
 }
 
@@ -521,83 +695,342 @@ RawBuffer CKMLogic::saveData(
        const Crypto::Data &data,
        const PolicySerializable &policy)
 {
-       int retCode = verifyAndSaveDataHelper(cred, name, owner, data, policy);
+       int retCode = verifyAndSaveObjectHelper(cred, name, owner, data, policy);
        return SerializeMessage(msgId, retCode, data.type);
 }
 
-RawBuffer CKMLogic::savePKCS12(
+template <typename Func, typename... Args>
+int CKMLogic::verifyObjectAndImportToStore(
        const Credentials &cred,
-       int msgId,
        const Name &name,
        const ClientId &owner,
-       const PKCS12Serializable &pkcs,
+       const Crypto::Data &object,
+       const PolicySerializable &policy,
+       TokenVector &tokens,
+       Func&& saveObjectFunc,
+       Args&&... saveObjectArgs)
+{
+       const auto retCode = verifyObject(object);
+       if (retCode != CKM_API_SUCCESS)
+               return retCode;
+
+       const auto hash = CryptoLogic::makeHash(name, owner, cred.clientUid, object.type);
+       if (hash.empty())
+               return CKM_API_ERROR_HASH_ERROR;
+
+       Token token{};
+
+       try
+       {
+               token = (this->*saveObjectFunc)(object, policy, hash,
+                                                                               std::forward<Args>(saveObjectArgs)...);
+       }
+       catch (const Exc::Exception& e)
+       {
+               LogError("Could not save token in store: " << e.what());
+               return e.error();
+       }
+
+       tokens.push_back(std::move(token));
+
+       return CKM_API_SUCCESS;
+};
+
+int CKMLogic::verifyPKCS12AndImportToStore(
+       const Credentials &cred,
+       const Name &name,
+       const ClientId &owner,
+       const Crypto::Data &keyObject,
        const PolicySerializable &keyPolicy,
-       const PolicySerializable &certPolicy)
+       const Crypto::Data &certObject,
+       const PolicySerializable &certPolicy,
+       const CKM::CertificateShPtrVector &caCertChain,
+       TokenVector &newTokens)
 {
-       return SerializeMessage(msgId, tryRet([&] {
-               auto [dbOp, retCode] = beginSave(cred, name, owner);
+       auto retCode = verifyObjectAndImportToStore(cred, name, owner, keyObject, keyPolicy,
+                                                                                               newTokens, &CKMLogic::importToStore);
+       if (retCode != CKM_API_SUCCESS)
+               return retCode;
+
+       retCode = verifyObjectAndImportToStore(cred, name, owner, certObject, certPolicy,
+                                                                                  newTokens, &CKMLogic::importToStore);
+       if (retCode != CKM_API_SUCCESS)
+       {
+               destroyInStore(newTokens);
+               return retCode;
+       }
+
+       size_t caCertIndex = 0;
+       for (const auto &caCert : caCertChain) {
+               const Crypto::Data caCertObject(DataType::getChainDatatype(caCertIndex++),
+                                                                               caCert->getDER());
+               retCode = verifyObjectAndImportToStore(cred, name, owner, caCertObject, certPolicy,
+                                                                                          newTokens, &CKMLogic::importToStore);
                if (retCode != CKM_API_SUCCESS)
+               {
+                       destroyInStore(newTokens);
                        return retCode;
+               }
+       }
 
-               // extract and encrypt the data
-               DB::RowVector encryptedRows;
+       return CKM_API_SUCCESS;
+};
 
-               auto import = [&](const Crypto::Data &data, const Policy& policy){
-                       retCode = verifyBinaryData(data);
-                       if (retCode != CKM_API_SUCCESS)
-                               return retCode;
+int CKMLogic::verifyPKCS12AndUpdateInStore(
+       const Credentials &cred,
+       const Name &name,
+       const ClientId &owner,
+       DBOperation &dbOp,
+       const Crypto::Data &newKeyObject,
+       const PolicySerializable &newKeyPolicy,
+       const Crypto::Data &newCertObject,
+       const PolicySerializable &newCertPolicy,
+       const CKM::CertificateShPtrVector &newCaCertChain,
+       DB::RowVector &oldPKCS12Rows,
+       TokenVector &newUpdatedPKCS12Tokens,
+       TokenVector &newSavedPKCS12Tokens)
+{
+       auto retCode = verifyObjectAndImportToStore(cred, name, owner, newKeyObject,
+                                                                                               newKeyPolicy, newUpdatedPKCS12Tokens,
+                                                                                               &CKMLogic::updateInStore,
+                                                                                               dbOp.handler(), oldPKCS12Rows[0]);
+       if (retCode != CKM_API_SUCCESS)
+               return retCode;
 
-                       auto digest = CryptoLogic::makeHash(name, owner, cred.clientUid, data.type);
-                       if (digest.empty())
-                               return CKM_API_ERROR_HASH_ERROR;
+       retCode = verifyObjectAndImportToStore(cred, name, owner, newCertObject,
+                                                                                  newCertPolicy, newUpdatedPKCS12Tokens,
+                                                                                  &CKMLogic::updateInStore,
+                                                                                  dbOp.handler(), oldPKCS12Rows[1]);
+       if (retCode != CKM_API_SUCCESS)
+       {
+               cleanupInStoreAndInDb(dbOp, name, owner, cred, oldPKCS12Rows, 1, newUpdatedPKCS12Tokens);
+               return retCode;
+       }
 
-                       Crypto::GStore &store = m_decider.getStore(data.type, policy);
+       size_t caCertChainIndex = 0;
+       for (size_t size = std::min(oldPKCS12Rows.size() - 2, newCaCertChain.size());
+               caCertChainIndex < size; ++caCertChainIndex)
+       {
+               const Crypto::Data newCaCertObject(DataType::getChainDatatype(caCertChainIndex),
+                                                                                  newCaCertChain[caCertChainIndex]->getDER());
+               retCode = verifyObjectAndImportToStore(cred, name, owner, newCaCertObject,
+                                                                                          newCertPolicy, newUpdatedPKCS12Tokens,
+                                                                                          &CKMLogic::updateInStore, dbOp.handler(),
+                                                                                          oldPKCS12Rows[caCertChainIndex + 2]);
+               if (retCode != CKM_API_SUCCESS)
+               {
+                       cleanupInStoreAndInDb(dbOp, name, owner, cred, oldPKCS12Rows,
+                                                                 caCertChainIndex + 2, newUpdatedPKCS12Tokens);
+                       return retCode;
+               }
+       }
 
-                       // do not encrypt data with password during cc_mode on
-                       Token token = store.import(data,
-                                                                          m_accessControl.isCCMode() ? "" : policy.password,
-                                                                          Crypto::EncryptionParams(), digest);
+       decryptAndDestroyInStore(name, owner, cred, oldPKCS12Rows,
+                                                        dbOp.handler(), caCertChainIndex + 2);
 
-                       encryptedRows.push_back(dbOp.encryptOne(std::move(token), policy));
-                       return CKM_API_SUCCESS;
-               };
+       if (caCertChainIndex < oldPKCS12Rows.size() - 2)
+       {
+               try
+               {
+                       dbOp.database().deleteRows(name, owner,
+                                                                          DataType::getChainDatatype(caCertChainIndex),
+                                                                          DataType::getChainDatatype(oldPKCS12Rows.size() - 3));
+               }
+               catch (const Exc::Exception& e)
+               {
+                       cleanupInStoreAndInDb(dbOp, name, owner, cred, {}, 0, newUpdatedPKCS12Tokens);
+                       throw;
+               }
+       }
+
+       for (size_t size = newCaCertChain.size(); caCertChainIndex < size; ++caCertChainIndex)
+       {
+               const Crypto::Data newCaCertObject(DataType::getChainDatatype(caCertChainIndex),
+                                                                                  newCaCertChain[caCertChainIndex]->getDER());
+               retCode = verifyObjectAndImportToStore(cred, name, owner, newCaCertObject, newCertPolicy,
+                                                                                          newSavedPKCS12Tokens, &CKMLogic::importToStore);
+               if (retCode != CKM_API_SUCCESS)
+               {
+                       cleanupInStoreAndInDb(dbOp, name, owner, cred, {}, 0,
+                                                                 newUpdatedPKCS12Tokens, newSavedPKCS12Tokens);
+                       return retCode;
+               }
+       }
+
+       return CKM_API_SUCCESS;
+};
+
+CKM::DB::RowVector CKMLogic::tokensToEncryptedRows(
+       const TokenVector &tokens,
+       const PolicySerializable &keyPolicy,
+       const PolicySerializable &certPolicy,
+       DBOperation &dbOp)
+{
+       CKM::DB::RowVector rows;
+
+       if (tokens.empty())
+               return rows;
+
+       rows.reserve(tokens.size());
+       rows.push_back(dbOp.encryptOne(Token(tokens[0]), keyPolicy));
+       for (size_t i = 1, size = tokens.size(); i < size; ++i)
+       {
+               rows.push_back(dbOp.encryptOne(Token(tokens[i]), certPolicy));
+       }
+
+       return rows;
+}
+
+void CKMLogic::encryptAndInsertInDb(
+       const Name &name,
+       const ClientId &owner,
+       const TokenVector &tokens,
+       const PolicySerializable &keyPolicy,
+       const PolicySerializable &certPolicy,
+       DBOperation &dbOp)
+{
+       try
+       {
+               const auto rows = tokensToEncryptedRows(tokens, keyPolicy, certPolicy, dbOp);
+               dbOp.database().saveRows(name, owner, rows);
+               dbOp.transaction().commit();
+       }
+       catch(const Exc::Exception& e)
+       {
+               destroyInStore(tokens);
+               throw;
+       }
+}
 
-               // private key is mandatory
-               auto key = pkcs.getKey();
+RawBuffer CKMLogic::savePKCS12(
+       const Credentials &cred,
+       int msgId,
+       const Name &name,
+       const ClientId &owner,
+       const PKCS12Serializable &pkcs,
+       const PolicySerializable &keyPolicy,
+       const PolicySerializable &certPolicy)
+{
+       return SerializeMessage(msgId, tryRet([&] {
+               const auto key = pkcs.getKey();
                if (!key) {
                        LogError("Failed to get private key from pkcs");
                        return CKM_API_ERROR_INVALID_FORMAT;
                }
+               const Crypto::Data keyObject(DataType(key->getType()), key->getDER());
 
-               Crypto::Data keyData(DataType(key->getType()), key->getDER());
-               retCode = import(keyData, keyPolicy);
-               if (retCode != CKM_API_SUCCESS)
-                       return retCode;
-
-               // certificate is mandatory
-               auto cert = pkcs.getCertificate();
+               const auto cert = pkcs.getCertificate();
                if (!cert) {
                        LogError("Failed to get certificate from pkcs");
                        return CKM_API_ERROR_INVALID_FORMAT;
                }
+               const Crypto::Data certObject(DataType::CERTIFICATE, cert->getDER());
+
+               const auto caCertChain = pkcs.getCaCertificateShPtrVector();
 
-               Crypto::Data certData(DataType::CERTIFICATE, cert->getDER());
-               retCode = import(certData, certPolicy);
+               auto [dbOp, retCode] = beginSave(cred, name, owner,
+                                                                                keyPolicy.overwritable && certPolicy.overwritable);
                if (retCode != CKM_API_SUCCESS)
                        return retCode;
 
-               // CA cert chain
-               unsigned int cert_index = 0;
-               for (const auto &ca : pkcs.getCaCertificateShPtrVector()) {
-                       Crypto::Data caCertData(DataType::getChainDatatype(cert_index ++), ca->getDER());
-                       retCode = import(caCertData, certPolicy);
+               TokenVector newUpdatedTokens;
+
+               if (!keyPolicy.overwritable || !certPolicy.overwritable)
+               {
+                       retCode = verifyPKCS12AndImportToStore(cred, name, owner, keyObject, keyPolicy,
+                                                                                                  certObject, certPolicy, caCertChain,
+                                                                                                  newUpdatedTokens);
                        if (retCode != CKM_API_SUCCESS)
                                return retCode;
+
+                       encryptAndInsertInDb(name, owner, newUpdatedTokens, keyPolicy, certPolicy, dbOp);
+                       return CKM_API_SUCCESS;
                }
 
-               // save the data
-               dbOp.database().saveRows(name, owner, encryptedRows);
-               dbOp.transaction().commit();
+               DB::RowVector oldRows;
+               dbOp.database().getRows(name,
+                                                               owner,
+                                                               DataType::DB_FIRST,
+                                                               DataType::DB_LAST,
+                                                               oldRows);
+               std::sort(oldRows.begin(), oldRows.end(), [](const auto& lhs, const auto& rhs){
+                       return lhs.dataType < rhs.dataType;
+               });
+
+               if (oldRows.empty())
+               {
+                       retCode = verifyPKCS12AndImportToStore(cred, name, owner, keyObject, keyPolicy,
+                                                                                                  certObject, certPolicy, caCertChain,
+                                                                                                  newUpdatedTokens);
+                       if (retCode != CKM_API_SUCCESS)
+                               return retCode;
+
+                       encryptAndInsertInDb(name, owner, newUpdatedTokens, keyPolicy, certPolicy, dbOp);
+                       return CKM_API_SUCCESS;
+               }
+
+               if (oldRows.size() == 1)
+               {
+                       dbOp.database().deleteRows(name, owner, oldRows[0].dataType, oldRows[0].dataType);
+
+                       const auto oldHash = CryptoLogic::makeHash(name, owner, cred.clientUid);
+                       if (oldHash.empty())
+                               return CKM_API_ERROR_HASH_ERROR;
+
+                       decryptAndDestroyInStore(dbOp.handler(), oldRows[0], oldHash);
+
+                       retCode = verifyPKCS12AndImportToStore(cred, name, owner, keyObject, keyPolicy,
+                                                                                                  certObject, certPolicy, caCertChain,
+                                                                                                  newUpdatedTokens);
+
+                       try
+                       {
+                               if (retCode != CKM_API_SUCCESS)
+                               {
+                                       newUpdatedTokens.clear();
+                                       dbOp.transaction().commit();
+                                       return retCode;
+                               }
+
+                               const auto rows = tokensToEncryptedRows(newUpdatedTokens, keyPolicy,
+                                                                                                               certPolicy, dbOp);
+                               dbOp.database().saveRows(rows);
+                               dbOp.database().resetPermissions(name, owner);
+                               dbOp.transaction().commit();
+                       }
+                       catch(const Exc::Exception& e)
+                       {
+                               cleanupInStoreAndInDb(dbOp, name, owner, cred, {}, 0, newUpdatedTokens);
+                               throw;
+                       }
+
+                       return CKM_API_SUCCESS;
+               }
+
+               TokenVector newSavedTokens;
+
+               retCode = verifyPKCS12AndUpdateInStore(cred, name, owner, dbOp, keyObject, keyPolicy,
+                                                                                          certObject, certPolicy, caCertChain,
+                                                                                          oldRows, newUpdatedTokens, newSavedTokens);
+               if (retCode != CKM_API_SUCCESS)
+                       return retCode;
+
+               try
+               {
+                       const auto newUpdatedRows = tokensToEncryptedRows(newUpdatedTokens, keyPolicy,
+                                                                                                                         certPolicy, dbOp);
+                       dbOp.database().updateRows(newUpdatedRows);
+                       const auto newSavedRows = tokensToEncryptedRows(newSavedTokens, keyPolicy,
+                                                                                                                       certPolicy, dbOp);
+                       dbOp.database().saveRows(newSavedRows);
+                       dbOp.database().resetPermissions(name, owner);
+                       dbOp.transaction().commit();
+               }
+               catch(const Exc::Exception& e)
+               {
+                       cleanupInStoreAndInDb(dbOp, name, owner, cred, {}, 0, newUpdatedTokens, newSavedTokens);
+                       throw;
+               }
 
                return CKM_API_SUCCESS;
        }));
@@ -1069,7 +1502,7 @@ int CKMLogic::importInitialData(
                                // Data are not encrypted, let's try to verify them
                                Crypto::Data binaryData;
 
-                               if (CKM_API_SUCCESS != (retCode = toBinaryData(data, binaryData)))
+                               if (CKM_API_SUCCESS != (retCode = toObject(data, binaryData)))
                                        return retCode;
 
                                token = store.import(binaryData,
@@ -1146,7 +1579,8 @@ std::tuple<CKMLogic::DBOperation, PermissionMask, int> CKMLogic::beginAndGetPerm
 std::tuple<CKMLogic::DBOperation, int> CKMLogic::beginSave(
        const Credentials &cred,
        const Name &name,
-       const ClientId &owner)
+       const ClientId &owner,
+       const bool overwritable)
 {
        auto [dbOp, retCode] = begin(cred, name, owner);
        if (retCode != CKM_API_SUCCESS)
@@ -1156,15 +1590,17 @@ std::tuple<CKMLogic::DBOperation, int> CKMLogic::beginSave(
        if (retCode != CKM_API_SUCCESS)
                return std::make_tuple(std::move(dbOp), retCode);
 
-       // check if accessor is allowed to save owner's items
        retCode = m_accessControl.canSave(cred, owner);
        if (retCode != CKM_API_SUCCESS) {
                LogDebug("accessor " << cred.client << " can not save rows owned by " << owner);
                return std::make_tuple(std::move(dbOp), retCode);
        }
 
-       if (dbOp.database().isNameOwnerPresent(name, owner))
-               retCode = CKM_API_ERROR_DB_ALIAS_EXISTS;
+       if (!overwritable)
+       {
+               if (dbOp.database().isNameOwnerPresent(name, owner))
+                       retCode = CKM_API_ERROR_DB_ALIAS_EXISTS;
+       }
 
        return std::make_tuple(std::move(dbOp), retCode);
 }
index ac81d77b30556a2b96dac4429a2f31db211eb3a8..ac49dc8d28b89a82bf669a3df2219840457355b2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2014-2021 Samsung Electronics Co., Ltd. All rights reserved
+ *  Copyright (c) 2014-2024 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -299,7 +299,7 @@ public:
                const ClientId &accessor,
                const PermissionMask permissionMask);
 
-       int verifyAndSaveDataHelper(
+       int verifyAndSaveObjectHelper(
                const Credentials &cred,
                const Name &name,
                const ClientId &owner,
@@ -439,7 +439,7 @@ private:
                }
 
                void finalize(Token&& token, const Policy& policy) {
-                       auto row = encryptOne(std::move(token), policy);
+                       const auto row = encryptOne(std::move(token), policy);
                        m_handler.database.saveRow(row);
                        m_transaction.commit();
                }
@@ -471,12 +471,110 @@ private:
        std::tuple<CKMLogic::DBOperation, int> beginSave(
                const Credentials &cred,
                const Name &name,
-               const ClientId &owner);
+               const ClientId &owner,
+               const bool overwritable = false);
        std::tuple<CKMLogic::DBOperation, RawBuffer, int> beginSaveAndGetHash(
                const Credentials &cred,
                const Name &name,
                const ClientId &owner);
 
+       Token importToStore(
+               const Crypto::Data &binaryData,
+               const PolicySerializable &policy,
+               const RawBuffer &hash);
+
+       void destroyInStore(
+               const Token &token);
+
+       void destroyInStore(
+               const TokenVector &tokens);
+
+       void decryptAndDestroyInStore(
+               CKM::UserData &handler,
+               DB::Row &row,
+               const RawBuffer &hash);
+
+       void decryptAndDestroyInStore(
+               const Name &name,
+               const ClientId &owner,
+               const Credentials &cred,
+               DB::RowVector &rows,
+               CKM::UserData &handler,
+               const size_t start = 0);
+
+       Token updateInStore(
+               const Crypto::Data &binaryData,
+               const PolicySerializable &policy,
+               const RawBuffer &hash,
+               CKM::UserData &handler,
+               DB::Row &row);
+
+       void importToStoreAndInsertInDb(
+               const Crypto::Data &object,
+               const PolicySerializable &policy,
+               const CKM::RawBuffer &hash,
+               DBOperation &dbOp);
+
+       template <typename... Args>
+       void cleanupInStoreAndInDb(
+               DBOperation &dbOp,
+               const Name &name,
+               const ClientId &owner,
+               const Credentials &cred,
+               DB::RowVector oldPKCS12Rows = {},
+               const size_t oldPKCS12RowsStartIndex = 0,
+               Args&&... tokens);
+
+       template <typename Func, typename... Args>
+       int verifyObjectAndImportToStore(
+               const Credentials &cred,
+               const Name &name,
+               const ClientId &owner,
+               const Crypto::Data &object,
+               const PolicySerializable &policy,
+               TokenVector &tokens,
+               Func&& saveObjectFunc,
+               Args&&... saveObjectArgs);
+
+       int verifyPKCS12AndImportToStore(
+               const Credentials &cred,
+               const Name &name,
+               const ClientId &owner,
+               const Crypto::Data &keyObject,
+               const PolicySerializable &keyPolicy,
+               const Crypto::Data &certObject,
+               const PolicySerializable &certPolicy,
+               const CKM::CertificateShPtrVector &caCertChain,
+               TokenVector &newTokens);
+
+       int verifyPKCS12AndUpdateInStore(
+               const Credentials &cred,
+               const Name &name,
+               const ClientId &owner,
+               DBOperation &dbOp,
+               const Crypto::Data &newKeyObject,
+               const PolicySerializable &newKeyPolicy,
+               const Crypto::Data &newCertObject,
+               const PolicySerializable &newCertPolicy,
+               const CKM::CertificateShPtrVector &newCaCertChain,
+               DB::RowVector &oldPKCS12Rows,
+               TokenVector &newUpdatedPKCS12Tokens,
+               TokenVector &newSavedPKCS12Tokens);
+
+       CKM::DB::RowVector tokensToEncryptedRows(
+               const TokenVector &tokens,
+               const PolicySerializable &keyPolicy,
+               const PolicySerializable &certPolicy,
+               DBOperation &dbOp);
+
+       void encryptAndInsertInDb(
+               const Name &name,
+               const ClientId &owner,
+               const TokenVector &tokens,
+               const PolicySerializable &keyPolicy,
+               const PolicySerializable &certPolicy,
+               DBOperation &dbOp);
+
        AccessControl m_accessControl;
        Crypto::Decider m_decider;
 
index 585436167fb186eac282d1ffc4fae7b9477b4371..7b17adcd5d414fb870822474a91133c962f461b9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2020 Samsung Electronics Co., Ltd. All rights reserved
+ * Copyright (c) 2014-2024 Samsung Electronics Co., Ltd. All rights reserved
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
  *    you may not use this file except in compliance with the License.
@@ -101,14 +101,32 @@ constexpr CommandBuffer OBJECT_INSERT_CMD_BUF{DB_CMD_OBJECT_INSERT_ID,
                                                "         );"};
 constexpr CommandBuffer OBJECT_UPDATE_CMD_BUF{DB_CMD_OBJECT_UPDATE_ID,
                                                "UPDATE OR FAIL OBJECTS SET"
+                                               "   exportable = ?001,"
                                                "   algorithmType = ?003,"
                                                "   encryptionScheme = ?004,"
                                                "   iv = ?005,"
                                                "   dataSize = ?006,"
                                                "   data = ?007,"
-                                               "   tag = ?008"
+                                               "   tag = ?008,"
+                                               "   backendId = ?009"
                                                "   WHERE idx IN (SELECT idx FROM NAMES WHERE name=?101 and label=?102)"
                                                "   AND dataType = ?002;"};
+constexpr CommandBuffer OBJECT_UPDATE_OR_REPLACE_CMD_BUF{DB_CMD_OBJECT_UPDATE_OR_REPLACE_ID,
+                                               "UPDATE OR REPLACE OBJECTS SET"
+                                               "   exportable = ?001,"
+                                               "   dataType = ?002,"
+                                               "   algorithmType = ?003,"
+                                               "   encryptionScheme = ?004,"
+                                               "   iv = ?005,"
+                                               "   dataSize = ?006,"
+                                               "   data = ?007,"
+                                               "   tag = ?008,"
+                                               "   backendId = ?009"
+                                               "   WHERE idx IN (SELECT idx FROM NAMES WHERE name=?101 and label=?102);"};
+constexpr CommandBuffer OBJECT_DELETE_CMD_BUF{DB_CMD_OBJECT_DELETE_ID,
+                                               "DELETE FROM OBJECTS"
+                                               "   WHERE (dataType BETWEEN ?101 AND ?102)"
+                                               "   AND idx=(SELECT idx FROM NAMES WHERE name=?001 and label=?002);"};
 constexpr CommandBuffer OBJECT_SELECT_BY_NAME_AND_OWNER_CMD_BUF{
                                                DB_CMD_OBJECT_SELECT_BY_NAME_AND_OWNER_ID,
                                                "SELECT * FROM [join_name_object_tables] "
@@ -132,6 +150,9 @@ constexpr CommandBuffer PERMISSION_SELECT_CMD_BUF{DB_CMD_PERMISSION_SELECT_ID,
 constexpr CommandBuffer PERMISSION_DELETE_CMD_BUF{DB_CMD_PERMISSION_DELETE_ID,
                                                "DELETE FROM PERMISSIONS WHERE permissionLabel=?104 AND "
                                                " idx=(SELECT idx FROM NAMES WHERE name=?101 and label=?102);"};
+constexpr CommandBuffer PERMISSION_RESET_CMD_BUF{DB_CMD_PERMISSION_RESET_ID,
+                                               "DELETE FROM PERMISSIONS WHERE permissionLabel!=?102 AND "
+                                               " idx=(SELECT idx FROM NAMES WHERE name=?101 and label=?102);"};
 /*
  * GROUP BY is necessary because of the following case:
  * -There are several permissions to L1, N1 (label, name) from other accessors. When listing
@@ -376,6 +397,27 @@ bool Crypto::isNameOwnerPresent(const Name &name, const ClientId &owner) const
                         "Couldn't check if name and owner pair is present");
 }
 
+void Crypto::saveRows(const RowVector &rows)
+{
+       try {
+               // transaction is present in the layer above
+               ObjectTable objectTable(m_connection.get());
+
+               for (const auto &row : rows)
+                       objectTable.addRow(row);
+
+               return;
+       } catch (const SqlConnection::Exception::SyntaxError &e) {
+               LogError("Couldn't prepare insert statement: " <<
+                                e.GetMessage());
+       } catch (const SqlConnection::Exception::InternalError &e) {
+               LogError("Couldn't execute insert statement: " <<
+                                e.GetMessage());
+       }
+
+       ThrowErr(Exc::DatabaseFailed, "Couldn't save Row");
+}
+
 void Crypto::saveRows(const Name &name, const ClientId &owner,
                                          const RowVector &rows)
 {
@@ -447,6 +489,44 @@ void Crypto::updateRow(const Row &row)
        ThrowErr(Exc::DatabaseFailed, "Couldn't update Row");
 }
 
+void Crypto::updateRows(const RowVector &rows)
+{
+       if (rows.empty())
+               return;
+
+       try {
+               // transaction is present in the layer above
+               ObjectTable objectTable(m_connection.get());
+
+               for (const auto &row : rows)
+                       objectTable.updateRow(row);
+
+               return;
+       } catch (const SqlConnection::Exception::SyntaxError &) {
+               LogError("Couldn't prepare update statement");
+       } catch (const SqlConnection::Exception::InternalError &) {
+               LogError("Couldn't execute update statement");
+       }
+
+       ThrowErr(Exc::DatabaseFailed, "Couldn't update Row");
+}
+
+void Crypto::updateOrReplaceRow(const Row &row)
+{
+       try {
+               // transaction is present in the layer above
+               ObjectTable objectTable(m_connection.get());
+               objectTable.updateOrReplaceRow(row);
+               return;
+       } catch (const SqlConnection::Exception::SyntaxError &) {
+               LogError("Couldn't prepare update statement");
+       } catch (const SqlConnection::Exception::InternalError &) {
+               LogError("Couldn't execute update statement");
+       }
+
+       ThrowErr(Exc::DatabaseFailed, "Couldn't update Row");
+}
+
 bool Crypto::deleteRow(
        const Name &name,
        const ClientId &owner)
@@ -726,6 +806,26 @@ void Crypto::deleteKey(const ClientId &owner)
        ThrowErr(Exc::DatabaseFailed, "Couldn't delete key for owner ", owner);
 }
 
+void Crypto::deleteRows(
+       const Name &name,
+       const ClientId &owner,
+       DataType typeRangeStart,
+       DataType typeRangeStop)
+{
+       try {
+               // transaction is present in the layer above
+               ObjectTable objectTable(m_connection.get());
+               objectTable.deleteRows(name, owner, typeRangeStart, typeRangeStop);
+               return;
+       } catch (const SqlConnection::Exception::SyntaxError &) {
+               LogError("Couldn't prepare delete objects statement");
+       } catch (const SqlConnection::Exception::InternalError &) {
+               LogError("Couldn't execute delete statement");
+       }
+
+       ThrowErr(Exc::DatabaseFailed, "Couldn't delete objects for owner ", owner);
+}
+
 void Crypto::setPermission(
        const Name &name,
        const ClientId &owner,
@@ -745,6 +845,24 @@ void Crypto::setPermission(
        ThrowErr(Exc::DatabaseFailed, "Couldn't set permissions for name ", name);
 }
 
+void Crypto::resetPermissions(
+       const Name &name,
+       const ClientId &owner)
+{
+       try {
+               // transaction is present in the layer above
+               PermissionTable permissionTable(m_connection.get());
+               permissionTable.resetPermissions(name, owner);
+               return;
+       } catch (const SqlConnection::Exception::SyntaxError &) {
+               LogError("Couldn't prepare delete statement");
+       } catch (const SqlConnection::Exception::InternalError &) {
+               LogError("Couldn't execute delete statement");
+       }
+
+       ThrowErr(Exc::DatabaseFailed, "Couldn't delete permissions for name ", name);
+}
+
 void Crypto::SchemaInfo::setVersionInfo()
 {
        auto insertContextCommand = m_connection->PrepareDataCommand(SCHEMA_SET_CMD_BUF);
@@ -811,6 +929,19 @@ PermissionMaskOptional Crypto::PermissionTable::getPermissionRow(
        return PermissionMaskOptional();
 }
 
+void Crypto::PermissionTable::resetPermissions(
+       const Name &name,
+       const ClientId &owner)
+{
+       auto resetCommand = m_connection->PrepareDataCommand(PERMISSION_RESET_CMD_BUF);
+
+       // name table reference
+       resetCommand->BindString(101, name.c_str());
+       resetCommand->BindString(102, owner.c_str());
+
+       resetCommand->Step();
+}
+
 void Crypto::NameTable::addRow(
        const Name &name,
        const ClientId &owner)
@@ -887,6 +1018,7 @@ void Crypto::ObjectTable::addRow(const Row &row)
 void Crypto::ObjectTable::updateRow(const Row &row)
 {
        auto updateObjectCommand = m_connection->PrepareDataCommand(OBJECT_UPDATE_CMD_BUF);
+       updateObjectCommand->BindInteger(1, row.exportable);
        updateObjectCommand->BindInteger(2, row.dataType);
        updateObjectCommand->BindInteger(3, static_cast<int>(row.algorithmType));
        updateObjectCommand->BindInteger(4, row.encryptionScheme);
@@ -894,6 +1026,7 @@ void Crypto::ObjectTable::updateRow(const Row &row)
        updateObjectCommand->BindInteger(6, row.dataSize);
        updateObjectCommand->BindBlob(7, row.data);
        updateObjectCommand->BindBlob(8, row.tag);
+       updateObjectCommand->BindInteger(9, static_cast<int>(row.backendId));
 
        // name table reference
        updateObjectCommand->BindString(101, row.name.c_str());
@@ -901,6 +1034,45 @@ void Crypto::ObjectTable::updateRow(const Row &row)
 
        updateObjectCommand->Step();
 }
+
+void Crypto::ObjectTable::updateOrReplaceRow(const Row &row)
+{
+       auto updateObjectCommand = m_connection->PrepareDataCommand(OBJECT_UPDATE_OR_REPLACE_CMD_BUF);
+       updateObjectCommand->BindInteger(1, row.exportable);
+       updateObjectCommand->BindInteger(2, row.dataType);
+       updateObjectCommand->BindInteger(3, static_cast<int>(row.algorithmType));
+       updateObjectCommand->BindInteger(4, row.encryptionScheme);
+       updateObjectCommand->BindBlob(5, row.iv);
+       updateObjectCommand->BindInteger(6, row.dataSize);
+       updateObjectCommand->BindBlob(7, row.data);
+       updateObjectCommand->BindBlob(8, row.tag);
+       updateObjectCommand->BindInteger(9, static_cast<int>(row.backendId));
+
+       // name table reference
+       updateObjectCommand->BindString(101, row.name.c_str());
+       updateObjectCommand->BindString(102, row.owner.c_str());
+
+       updateObjectCommand->Step();
+}
+
+void Crypto::ObjectTable::deleteRows(
+       const Name &name,
+       const ClientId &owner,
+       DataType typeRangeStart,
+       DataType typeRangeStop)
+{
+       auto deleteCommand = m_connection->PrepareDataCommand(OBJECT_DELETE_CMD_BUF);
+
+       // name table reference
+       deleteCommand->BindString(1, name.c_str());
+       deleteCommand->BindString(2, owner.c_str());
+
+       deleteCommand->BindInteger(101, typeRangeStart);
+       deleteCommand->BindInteger(102, typeRangeStop);
+
+       deleteCommand->Step();
+}
+
 } // namespace DB
 } // namespace CKM
 
index ee1441f6fe9a4ee3a3534b6063624db624339439..554068823a9e6f50f3e9c53d1802a9882f66d745 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2020 Samsung Electronics Co., Ltd. All rights reserved
+ * Copyright (c) 2014-2024 Samsung Electronics Co., Ltd. All rights reserved
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
  *    you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@
 namespace CKM {
 
 class CKMLogicExt;
+class CKMLogicFail;
 
 namespace DB {
 class Crypto {
@@ -62,6 +63,9 @@ public:
        void saveRow(
                const Row &row);
 
+       void saveRows(
+               const RowVector &rows);
+
        void saveRows(
                const Name &name,
                const ClientId &owner,
@@ -70,6 +74,12 @@ public:
        void updateRow(
                const Row &row);
 
+       void updateRows(
+               const RowVector &rows);
+
+       void updateOrReplaceRow(
+               const Row &row);
+
        bool isNameOwnerPresent(
                const Name &name,
                const ClientId &owner) const;
@@ -113,6 +123,12 @@ public:
                const Name &name,
                const ClientId &owner);
 
+       void deleteRows(
+               const Name &name,
+               const ClientId &owner,
+               DataType typeRangeStart,
+               DataType typeRangeStop);
+
        // keys
        void saveKey(const ClientId &owner, const RawBuffer &key);
        RawBufferOptional getKey(const ClientId &owner);
@@ -130,6 +146,10 @@ public:
                const ClientId &owner,
                const ClientId &accessor) const;
 
+       void resetPermissions(
+               const Name &name,
+               const ClientId &owner);
+
        class Transaction {
        public:
                explicit Transaction(Crypto *db) : m_db(db), m_inTransaction(false)
@@ -225,6 +245,7 @@ private:
        bool m_inUserTransaction;
 
        friend CKMLogicExt;
+       friend CKMLogicFail;
 
        void resetDB();
        void initDatabase();
@@ -285,6 +306,13 @@ public:
                        const Row &row);
                void updateRow(
                        const Row &row);
+               void updateOrReplaceRow(
+                       const Row &row);
+               void deleteRows(
+                       const Name &name,
+                       const ClientId &owner,
+                       DataType typeRangeStart,
+                       DataType typeRangeStop);
 
        private:
                SqlConnection *m_connection;
@@ -306,6 +334,10 @@ public:
                        const ClientId &owner,
                        const ClientId &accessor) const;
 
+               void resetPermissions(
+                       const Name &name,
+                       const ClientId &owner);
+
        private:
                SqlConnection *m_connection;
        };
index 44ca12f1b715b913e7cbda765f6f4259a7111ee4..0fd568038fff4740c939ea0497d56e2d7ce16899 100644 (file)
@@ -31,6 +31,7 @@ SET(PKCS12_TEST_DIR ${UNIT_TESTS_DIR}/pkcs12/)
 ADD_DEFINITIONS("-DDB_TEST_DIR=\"${DB_TEST_DIR}\"")
 ADD_DEFINITIONS("-DSS_TEST_DIR=\"${SS_TEST_DIR}\"")
 ADD_DEFINITIONS("-DPKCS12_TEST_DIR=\"${PKCS12_TEST_DIR}\"")
+ADD_DEFINITIONS("-DMISC_DIR=\"${MISC_DIR}\"")
 
 ADD_DEFINITIONS("-DBOOST_TEST_DYN_LINK")
 ADD_DEFINITIONS("-DOVERRIDE_SOCKET_TIMEOUT=10")
@@ -43,6 +44,7 @@ INCLUDE_DIRECTORIES(SYSTEM ${KEY_MANAGER_DEP_INCLUDE_DIRS})
 
 INCLUDE_DIRECTORIES(
     ${MANAGER_PATH}/dpl/db/include
+    ${MANAGER_PATH}/dpl/db/include/dpl/db
     ${MANAGER_PATH}/dpl/core/include
     ${MANAGER_PATH}/dpl/log/include
     ${MANAGER_PATH}/service
@@ -97,6 +99,7 @@ SET(UNIT_TESTS_SOURCES
     ${CMAKE_CURRENT_SOURCE_DIR}/test_sw-backend.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/test_xml-parser.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/test_xml-utils.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/test_ckm_logic.cpp
 
     ${MANAGER_PATH}/client/client-common.cpp
     ${MANAGER_PATH}/client-async/descriptor-set.cpp
@@ -141,6 +144,14 @@ SET(UNIT_TESTS_SOURCES
     ${MANAGER_PATH}/service/for-each-file.cpp
     ${MANAGER_PATH}/service/key-provider.cpp
     ${MANAGER_PATH}/service/ss-crypto.cpp
+    ${MANAGER_PATH}/service/access-control.cpp
+    ${MANAGER_PATH}/service/certificate-config.cpp
+    ${MANAGER_PATH}/service/certificate-store.cpp
+    ${MANAGER_PATH}/service/ckm-logic.cpp
+    ${MANAGER_PATH}/service/file-lock.cpp
+    ${MANAGER_PATH}/service/file-system.cpp
+    ${MANAGER_PATH}/service/ss-migrate.cpp
+    ${MANAGER_PATH}/service/permission.cpp
     ${SE_BACKEND_SOURCES}
 )
 
diff --git a/unit-tests/test_ckm_logic.cpp b/unit-tests/test_ckm_logic.cpp
new file mode 100644 (file)
index 0000000..0deb355
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ *  Copyright (c) 2024 Samsung Electronics Co., Ltd. All rights reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ *
+ * @file        test_ckm_logic.cpp
+ * @author      Andrei Vakulich (a.vakulich@samsung.com)
+ * @version
+ * @brief
+ */
+
+#include <fstream>
+#include <utility>
+#include <iostream>
+#include <boost_macros_wrapper.h>
+#include <ckm/ckm-type.h>
+#include <test_common.h>
+#include <ckm-logic.h>
+#include <sql_connection.h>
+#include <timer.h>
+
+using namespace CKM;
+
+namespace {
+
+ClientId generateOwner(const size_t id)
+{
+    std::stringstream ss;
+    ss << "OWNER_" << id;
+    return ss.str();
+}
+
+PKCS12ShPtr readPKCS12(const std::string &path)
+{
+    std::ifstream is(path);
+    BOOST_REQUIRE(is);
+
+    std::istreambuf_iterator<char> begin(is), end;
+    RawBuffer pkcsBuffer(begin, end);
+    const auto pkcs = PKCS12::create(pkcsBuffer, Password());
+    BOOST_REQUIRE(pkcs);
+    BOOST_REQUIRE(!pkcs->empty());
+
+    return pkcs;
+}
+
+int getRetCodeSaveData(RawBuffer&& response)
+{
+    MessageBuffer message;
+    message.Push(std::move(response));
+    int msgId = 0, retCode = 0;
+    DataType dataType;
+    message.Deserialize(msgId, retCode, dataType);
+    return retCode;
+}
+
+int getRetCodeSavePKCS12(RawBuffer&& response)
+{
+    MessageBuffer message;
+    message.Push(std::move(response));
+    int msgId = 0, retCode = 0;
+    message.Deserialize(msgId, retCode);
+    return retCode;
+}
+
+int getRetCodeRemoveData(RawBuffer&& response)
+{
+    return getRetCodeSavePKCS12(std::move(response));
+}
+
+const Name NAME = "NAME";
+constexpr uid_t CLIENT_UID = 5001;
+const ClientId CLIENT_ID = generateOwner(0);
+const Password PASSWORD = "";
+const Credentials CREDENTIALS(CLIENT_UID, CLIENT_ID);
+const Policy OVERWRITABLE_POLICY(PASSWORD, false, PolicyBackend::DEFAULT, true);
+const Policy NON_OVERWRITABLE_POLICY(PASSWORD, false, PolicyBackend::DEFAULT, false);
+
+int messageId = 0;
+
+const DataType DATA_TYPE = DataType::Type::BINARY_DATA;
+constexpr unsigned char DATA[] = { "DATA" };
+const RawBuffer RAW_DATA( DATA, DATA + strlen((char*)DATA) );
+
+const DataType KEY_RSA_PUBLIC_TYPE = DataType::Type::KEY_RSA_PUBLIC;
+constexpr unsigned char KEY_RSA_PUBLIC[] = {
+    "-----BEGIN PUBLIC KEY-----\n"
+    "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMP6sKttnQ58BAi27b8X+8KVQt\n"
+    "JgpJhhCF0RtWaTVqAhVDG3y4x6IuAvXDtPSjLe/2E01fYGVxNComPJOmUOfUD06B\n"
+    "CWPYH2+7jOfQIOy/TMlt+W7xfou9rqnPRoKRaodoLqH5WK0ahkntWCAjstoKZoG+\n"
+    "3Op0tEjy0jpmzeyNiQIDAQAB\n"
+    "-----END PUBLIC KEY-----"
+};
+const RawBuffer RAW_KEY_RSA_PUBLIC( KEY_RSA_PUBLIC,
+                                    KEY_RSA_PUBLIC + strlen((char*)KEY_RSA_PUBLIC) );
+
+const auto pkcs = readPKCS12(MISC_DIR "/encryption-scheme.p12");
+
+} // namespace
+
+namespace CKM {
+
+namespace DB {
+
+class FailedSqlConnection : public DB::SqlConnection
+{
+public:
+    FailedSqlConnection(DB::SqlConnection&& sqlConnection)
+    {
+        m_connection = std::exchange(sqlConnection.m_connection, nullptr);
+        m_isKeySet = std::exchange(sqlConnection.m_isKeySet, false);
+        m_synchronizationObject = std::exchange(sqlConnection.m_synchronizationObject, nullptr);
+    }
+
+    void CommitTransaction() override
+    {
+        ThrowMsg(Exc::InternalError, "Failed transaction commit");
+    }
+};
+
+}
+
+class CKMLogicFail : public CKMLogic
+{
+public:
+    using CKMLogic::saveData;
+    using CKMLogic::savePKCS12;
+
+    CKMLogicFail(
+        const uid_t clientUid,
+        const CKM::Credentials &credentials,
+        const CKM::Name &name,
+        const CKM::ClientId &clientId) :
+        m_clientUid(clientUid),
+        m_credentials(credentials),
+        m_name(name),
+        m_clientId(clientId)
+    {}
+
+    void setFailedConnection()
+    {
+        auto connection = std::make_unique<CKM::DB::FailedSqlConnection>(
+            std::move(*m_userDataMap.at(m_clientUid).database.m_connection)
+        );
+
+        m_userDataMap.at(m_clientUid).database.m_connection = std::move(connection);
+    }
+
+    bool isNameOwnerPresent(const CKM::Name &name, const CKM::ClientId &owner) const
+    {
+        return m_userDataMap.at(m_clientUid).database.isNameOwnerPresent(name, owner);
+    }
+
+    int saveData(const Crypto::Data &data, const PolicySerializable &policy)
+    {
+        auto response = saveData(m_credentials, ++messageId, m_name, m_clientId,
+                                 data, policy);
+
+        return getRetCodeSaveData(std::move(response));
+    }
+
+    int savePKCS12(
+        const PKCS12Serializable &pkcs12,
+        const PolicySerializable &keyPolicy,
+        const PolicySerializable &certPolicy)
+    {
+        auto response = savePKCS12(m_credentials, ++messageId, m_name, m_clientId,
+                                   pkcs12, keyPolicy, certPolicy);
+
+        return getRetCodeSavePKCS12(std::move(response));
+    }
+
+private:
+    const uid_t m_clientUid;
+    const CKM::Credentials m_credentials;
+    const CKM::Name m_name;
+    const CKM::ClientId m_clientId;
+};
+
+}
+
+namespace {
+
+class CKMLogicFailFixture
+{
+public:
+    CKMLogicFailFixture()
+        : logicFail(CLIENT_UID, CREDENTIALS, NAME, CLIENT_ID)
+    {
+        logicFail.unlockUserKey(CLIENT_UID, PASSWORD);
+        logicFail.removeData(CREDENTIALS, ++messageId, NAME, CLIENT_ID);
+    }
+
+    ~CKMLogicFailFixture()
+    {
+        logicFail.lockUserKey(CLIENT_UID);
+    }
+
+    CKMLogicFail logicFail;
+};
+
+} // namespace
+
+BOOST_AUTO_TEST_SUITE(CKMLOGIC_TEST)
+
+BOOST_FIXTURE_TEST_CASE(DBTestSaveApi, CKMLogicFailFixture)
+{
+    logicFail.setFailedConnection();
+
+    auto retCode = logicFail.saveData(Crypto::Data(DATA_TYPE, RAW_DATA),
+                                      PolicySerializable(NON_OVERWRITABLE_POLICY));
+    BOOST_REQUIRE(retCode == CKM_API_ERROR_SERVER_ERROR);
+    BOOST_REQUIRE(!logicFail.isNameOwnerPresent(NAME, CLIENT_ID));
+
+    retCode = logicFail.saveData(Crypto::Data(DATA_TYPE, RAW_DATA),
+                                 PolicySerializable(OVERWRITABLE_POLICY));
+    BOOST_REQUIRE(retCode == CKM_API_ERROR_SERVER_ERROR);
+    BOOST_REQUIRE(!logicFail.isNameOwnerPresent(NAME, CLIENT_ID));
+}
+
+BOOST_FIXTURE_TEST_CASE(DBTestSaveDataUpdateDataApi, CKMLogicFailFixture)
+{
+    auto retCode = logicFail.saveData(Crypto::Data(DATA_TYPE, RAW_DATA),
+                                      PolicySerializable(NON_OVERWRITABLE_POLICY));
+    BOOST_REQUIRE(retCode == CKM_API_SUCCESS);
+    BOOST_REQUIRE(logicFail.isNameOwnerPresent(NAME, CLIENT_ID));
+
+    logicFail.setFailedConnection();
+
+    retCode = logicFail.saveData(Crypto::Data(DATA_TYPE, RAW_DATA),
+                                 PolicySerializable(OVERWRITABLE_POLICY));
+    BOOST_REQUIRE(retCode == CKM_API_ERROR_SERVER_ERROR);
+    BOOST_REQUIRE(!logicFail.isNameOwnerPresent(NAME, CLIENT_ID));
+}
+
+BOOST_FIXTURE_TEST_CASE(DBTestSaveKeyUpdateDataApi, CKMLogicFailFixture)
+{
+    auto retCode = logicFail.saveData(Crypto::Data(KEY_RSA_PUBLIC_TYPE, RAW_KEY_RSA_PUBLIC),
+                                      PolicySerializable(NON_OVERWRITABLE_POLICY));
+    BOOST_REQUIRE(retCode == CKM_API_SUCCESS);
+    BOOST_REQUIRE(logicFail.isNameOwnerPresent(NAME, CLIENT_ID));
+
+    logicFail.setFailedConnection();
+
+    retCode = logicFail.saveData(Crypto::Data(DATA_TYPE, RAW_DATA),
+                                      PolicySerializable(OVERWRITABLE_POLICY));
+    BOOST_REQUIRE(retCode == CKM_API_ERROR_SERVER_ERROR);
+    BOOST_REQUIRE(!logicFail.isNameOwnerPresent(NAME, CLIENT_ID));
+}
+
+BOOST_FIXTURE_TEST_CASE(DBTestSavePKCS12UpdateDataApi, CKMLogicFailFixture)
+{
+    auto retCode = logicFail.savePKCS12(PKCS12Serializable(*pkcs.get()),
+                                        PolicySerializable(NON_OVERWRITABLE_POLICY),
+                                        PolicySerializable(NON_OVERWRITABLE_POLICY));
+    BOOST_REQUIRE(retCode == CKM_API_SUCCESS);
+    BOOST_REQUIRE(logicFail.isNameOwnerPresent(NAME, CLIENT_ID));
+
+    logicFail.setFailedConnection();
+
+    retCode = logicFail.saveData(Crypto::Data(DATA_TYPE, RAW_DATA),
+                                 PolicySerializable(OVERWRITABLE_POLICY));
+    BOOST_REQUIRE(retCode == CKM_API_ERROR_SERVER_ERROR);
+    BOOST_REQUIRE(!logicFail.isNameOwnerPresent(NAME, CLIENT_ID));
+}
+
+BOOST_FIXTURE_TEST_CASE(DBTestSaveDataUpdatePKCS12Api, CKMLogicFailFixture)
+{
+    auto retCode = logicFail.saveData(Crypto::Data(DATA_TYPE, RAW_DATA),
+                                      PolicySerializable(NON_OVERWRITABLE_POLICY));
+    BOOST_REQUIRE(retCode == CKM_API_SUCCESS);
+    BOOST_REQUIRE(logicFail.isNameOwnerPresent(NAME, CLIENT_ID));
+
+    logicFail.setFailedConnection();
+
+    retCode = logicFail.savePKCS12(PKCS12Serializable(*pkcs.get()),
+                                   PolicySerializable(OVERWRITABLE_POLICY),
+                                   PolicySerializable(OVERWRITABLE_POLICY));
+    BOOST_REQUIRE(retCode == CKM_API_ERROR_SERVER_ERROR);
+    BOOST_REQUIRE(!logicFail.isNameOwnerPresent(NAME, CLIENT_ID));
+}
+
+BOOST_FIXTURE_TEST_CASE(DBTestSavePKCS12UpdatePKCS12Api, CKMLogicFailFixture)
+{
+    auto retCode = logicFail.savePKCS12(PKCS12Serializable(*pkcs.get()),
+                                        PolicySerializable(NON_OVERWRITABLE_POLICY),
+                                        PolicySerializable(NON_OVERWRITABLE_POLICY));
+    BOOST_REQUIRE(retCode == CKM_API_SUCCESS);
+    BOOST_REQUIRE(logicFail.isNameOwnerPresent(NAME, CLIENT_ID));
+
+    logicFail.setFailedConnection();
+
+    retCode = logicFail.savePKCS12(PKCS12Serializable(*pkcs.get()),
+                                   PolicySerializable(OVERWRITABLE_POLICY),
+                                   PolicySerializable(OVERWRITABLE_POLICY));
+    BOOST_REQUIRE(retCode == CKM_API_ERROR_SERVER_ERROR);
+    BOOST_REQUIRE(!logicFail.isNameOwnerPresent(NAME, CLIENT_ID));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+namespace {
+
+class CKMLogicFixture
+{
+public:
+    CKMLogicFixture()
+    {
+        logic.unlockUserKey(CLIENT_UID, PASSWORD);
+    }
+
+    ~CKMLogicFixture()
+    {
+        logic.lockUserKey(CLIENT_UID);
+    }
+
+    CKMLogic logic{};
+};
+
+void generateData(CKMLogic& logic, const size_t dataCount, const size_t pkcs12Count)
+{
+    for (size_t i = 0; i < dataCount; ++i)
+    {
+        const auto clientId = generateOwner(i);
+        auto response = logic.saveData(Credentials(CLIENT_UID, clientId), ++messageId, NAME,
+                                       clientId, Crypto::Data(DATA_TYPE, RAW_DATA),
+                                       PolicySerializable(OVERWRITABLE_POLICY));
+        BOOST_REQUIRE(getRetCodeSaveData(std::move(response)) == CKM_API_SUCCESS);
+    }
+
+    for (size_t i = dataCount; i < dataCount + pkcs12Count; ++i)
+    {
+        const auto clientId = generateOwner(i);
+        auto response = logic.savePKCS12(Credentials(CLIENT_UID, clientId), ++messageId, NAME,
+                                         clientId, PKCS12Serializable(*pkcs.get()),
+                                         PolicySerializable(OVERWRITABLE_POLICY),
+                                         PolicySerializable(OVERWRITABLE_POLICY));
+        BOOST_REQUIRE(getRetCodeSavePKCS12(std::move(response)) == CKM_API_SUCCESS);
+    }
+}
+
+void removeGeneratedData(CKMLogic& logic, const size_t dataCount, const size_t pkcs12Count)
+{
+    for (size_t i = 0; i < dataCount + pkcs12Count; ++i)
+    {
+        const auto clientId = generateOwner(i);
+        auto response = logic.removeData(Credentials(CLIENT_UID, clientId), ++messageId,
+                                         NAME, clientId);
+        BOOST_REQUIRE(getRetCodeRemoveData(std::move(response)) == CKM_API_SUCCESS);
+    }
+}
+
+double measureSaveApi(
+    CKMLogic& logic,
+    const size_t dataCount,
+    const bool isPKCS12,
+    const Policy &policy,
+    bool isNeedToRemove)
+{
+    if (dataCount == 0) return 0;
+
+    size_t averageSaveApiTime = 0;
+
+    for (size_t i = 0; i < dataCount; ++i)
+    {
+        const auto clientId = generateOwner(i);
+        Credentials credentials(CLIENT_UID, clientId);
+
+        if (isNeedToRemove)
+        {
+            auto [response, time] = measure(&CKMLogic::removeData, logic,
+                                            credentials, ++messageId, NAME, clientId);
+            BOOST_REQUIRE(getRetCodeRemoveData(std::move(response)) == CKM_API_SUCCESS);
+            averageSaveApiTime += time;
+        }
+
+        if (isPKCS12)
+        {
+            auto [response, time] = measure(&CKMLogic::savePKCS12, logic, credentials,
+                                            ++messageId, NAME, clientId,
+                                            PKCS12Serializable(*pkcs.get()),
+                                            PolicySerializable(policy),
+                                            PolicySerializable(policy));
+            BOOST_REQUIRE(getRetCodeSavePKCS12(std::move(response)) == CKM_API_SUCCESS);
+            averageSaveApiTime += time;
+        }
+        else
+        {
+            auto [response, time] = measure(&CKMLogic::saveData, logic,
+                                            credentials, ++messageId, NAME, clientId,
+                                            Crypto::Data(DATA_TYPE, RAW_DATA),
+                                            PolicySerializable(policy));
+            BOOST_REQUIRE(getRetCodeSaveData(std::move(response)) == CKM_API_SUCCESS);
+            averageSaveApiTime += time;
+        }
+    }
+
+    return averageSaveApiTime / (double)dataCount;
+}
+
+void printTime(const size_t dataCount, const size_t pkcs12Count,
+    const int64_t averageSaveApiTime, const int64_t averageUpdateApiTime)
+{
+    std::cout << "data=" << dataCount << " pkcs12=" << pkcs12Count << "\n"
+        << "remove + save: " << averageSaveApiTime << "ms\n"
+        << "update: " << averageUpdateApiTime << "ms\n" << std::endl;
+}
+
+void doBenchmark(CKMLogic& logic, const size_t dataCount,
+    const size_t pkcs12Count, const bool isPKCS12)
+{
+    generateData(logic, dataCount, pkcs12Count);
+    const auto averageSaveTime = measureSaveApi(logic, dataCount + pkcs12Count,
+                                                isPKCS12, NON_OVERWRITABLE_POLICY, true);
+    removeGeneratedData(logic, dataCount, pkcs12Count);
+
+    generateData(logic, dataCount, pkcs12Count);
+    const auto averageUpdateTime = measureSaveApi(logic, dataCount + pkcs12Count,
+                                                  isPKCS12, OVERWRITABLE_POLICY, false);
+    removeGeneratedData(logic, dataCount, pkcs12Count);
+
+    printTime(dataCount, pkcs12Count, averageSaveTime, averageUpdateTime);
+}
+
+} // namespace
+
+BOOST_FIXTURE_TEST_SUITE(UPDATE_API_BENCHMARKS, CKMLogicFixture)
+
+POSITIVE_TEST_CASE(UpdateDataWithData)
+{
+    doBenchmark(logic, 1, 0, false);
+    doBenchmark(logic, 10, 0, false);
+    doBenchmark(logic, 50, 0, false);
+    doBenchmark(logic, 100, 0, false);
+}
+
+POSITIVE_TEST_CASE(UpdatePKCS12WithData)
+{
+    doBenchmark(logic, 0, 1, false);
+    doBenchmark(logic, 0, 10, false);
+    doBenchmark(logic, 0, 50, false);
+    doBenchmark(logic, 0, 100, false);
+}
+
+POSITIVE_TEST_CASE(UpdateDataAndPKCS12WithData)
+{
+    doBenchmark(logic, 1, 1, false);
+    doBenchmark(logic, 10, 10, false);
+    doBenchmark(logic, 50, 50, false);
+    doBenchmark(logic, 100, 100, false);
+}
+
+POSITIVE_TEST_CASE(UpdateDataAndPKCS12WithPKCS12)
+{
+    doBenchmark(logic, 1, 1, true);
+    doBenchmark(logic, 10, 10, true);
+    doBenchmark(logic, 50, 50, true);
+    doBenchmark(logic, 100, 100, true);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/unit-tests/timer.h b/unit-tests/timer.h
new file mode 100644 (file)
index 0000000..e61dd57
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *  Copyright (c) 2024 Samsung Electronics Co., Ltd. All rights reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ *
+ *
+ * @file        timer.h
+ * @author      Andrei Vakulich (a.vakulich@samsung.com)
+ * @version
+ * @brief
+ */
+
+#pragma once
+
+#include <cassert>
+#include <chrono>
+#include <functional>
+
+namespace CKM {
+
+class Timer {
+public:
+       Timer() = default;
+
+       void start() {
+               assert(m_state != State::Started);
+               m_state = State::Started;
+               m_start = std::chrono::high_resolution_clock::now();
+       }
+
+       void stop() {
+               assert(m_state == State::Started);
+               m_state = State::Stopped;
+               m_finish = std::chrono::high_resolution_clock::now();
+       }
+
+       auto elapsedMs() {
+               assert(m_state == State::Stopped);
+               const auto elps = m_finish - m_start;
+               const auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(elps);
+               return msec.count();
+       }
+
+private:
+       enum class State : std::uint8_t {
+               Cold = 0,
+               Started,
+               Stopped
+       };
+       State m_state = State::Cold;
+
+       std::chrono::high_resolution_clock::time_point m_start, m_finish;
+};
+
+template <typename T, typename... Args>
+auto measure(T callable, Args&&... args) {
+       Timer timer;
+       timer.start();
+       const auto result = std::invoke(callable, std::forward<Args>(args)...);
+       timer.stop();
+       return std::pair{ result, timer.elapsedMs() };
+}
+
+}