};
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 {
/*
- * 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.
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]
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]
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.
*
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]
/*
- * 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.
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)
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
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)
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
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
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
Serialization::Serialize(stream, password);
Serialization::Serialize(stream, extractable);
Serialization::Serialize(stream, backend);
+ Serialization::Serialize(stream, overwritable);
}
void PolicySerializable::Deserialize(IStream &stream)
Deserialization::Deserialize(stream, password);
Deserialization::Deserialize(stream, extractable);
Deserialization::Deserialize(stream, backend);
+ Deserialization::Deserialize(stream, overwritable);
}
PKCS12Serializable::PKCS12Serializable()
};
using TokenPair = std::pair<Token, Token>;
+using TokenVector = std::vector<Token>;
} // namespace CKM
/*
- * 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.
virtual void destroy(const Token &) = 0;
virtual size_t maxChunkSize() const { return 0; }
+ CryptoBackend getBackendId() const { return m_backendId; }
+
virtual ~GStore() {}
protected:
namespace CKM {
namespace DB {
+class FailedSqlConnection;
+
enum DBCommandID {
DB_CMD_SCHEMA_SET_ID,
DB_CMD_SCHEMA_GET_ID,
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,
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,
* Execute COMMIT; command to commit changes in database
*
*/
- void CommitTransaction();
+ virtual void CommitTransaction();
/**
* Prepare stored procedure
private:
void ExecCommandHelper(Output *out, const char *format, va_list args);
+
+ friend FailedSqlConnection;
};
RawBuffer createHexPass(const RawBuffer &rawPass);
} // namespace DB
}
}
-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()) {
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,
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 {
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)
}));
}
-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;
});
}
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;
}));
// 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,
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)
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);
}
/*
- * 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.
const ClientId &accessor,
const PermissionMask permissionMask);
- int verifyAndSaveDataHelper(
+ int verifyAndSaveObjectHelper(
const Credentials &cred,
const Name &name,
const ClientId &owner,
}
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();
}
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;
/*
- * 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.
" );"};
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] "
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
"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)
{
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)
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,
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);
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)
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);
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->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
/*
- * 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.
namespace CKM {
class CKMLogicExt;
+class CKMLogicFail;
namespace DB {
class Crypto {
void saveRow(
const Row &row);
+ void saveRows(
+ const RowVector &rows);
+
void saveRows(
const Name &name,
const ClientId &owner,
void updateRow(
const Row &row);
+ void updateRows(
+ const RowVector &rows);
+
+ void updateOrReplaceRow(
+ const Row &row);
+
bool isNameOwnerPresent(
const Name &name,
const ClientId &owner) const;
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);
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)
bool m_inUserTransaction;
friend CKMLogicExt;
+ friend CKMLogicFail;
void resetDB();
void initDatabase();
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;
const ClientId &owner,
const ClientId &accessor) const;
+ void resetPermissions(
+ const Name &name,
+ const ClientId &owner);
+
private:
SqlConnection *m_connection;
};
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")
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
${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
${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}
)
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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() };
+}
+
+}