diff --git a/native/cpp/CommonCpp/DatabaseManagers/DatabaseManager.h b/native/cpp/CommonCpp/DatabaseManagers/DatabaseManager.h --- a/native/cpp/CommonCpp/DatabaseManagers/DatabaseManager.h +++ b/native/cpp/CommonCpp/DatabaseManagers/DatabaseManager.h @@ -13,9 +13,21 @@ // which is identical to finishing the migration process and having a fully // operational database that can be used by application logic. static std::once_flag queryExecutorCreationIndicated; + static void indicateQueryExecutorCreation(); + + // Indicate that all properties needed to create an instance of + // SQLiteQueryExecutor were initialized in a thread-safe way (keys were read + // from SecureStore or generated). + static std::once_flag sqliteQueryExecutorPropertiesInitialized; + static void + initializeSQLiteQueryExecutorProperties(std::string &databasePath); + + // Generate and persist a backup key used as a database encryption key. + static std::string generateBackupDataKey(); + // Generate and persist key used for encrypt backup logs. + static std::string generateBackupLogDataKey(); static void setDatabaseStatusAsWorkable(); - static void indicateQueryExecutorCreation(); public: static const DatabaseQueryExecutor &getQueryExecutor(); diff --git a/native/cpp/CommonCpp/DatabaseManagers/DatabaseManager.cpp b/native/cpp/CommonCpp/DatabaseManagers/DatabaseManager.cpp --- a/native/cpp/CommonCpp/DatabaseManagers/DatabaseManager.cpp +++ b/native/cpp/CommonCpp/DatabaseManagers/DatabaseManager.cpp @@ -5,10 +5,12 @@ #include "Logger.h" #include "PlatformSpecificTools.h" #include "SQLiteQueryExecutor.h" +#include "SQLiteUtils.h" namespace comm { std::once_flag DatabaseManager::queryExecutorCreationIndicated; +std::once_flag DatabaseManager::sqliteQueryExecutorPropertiesInitialized; typedef const std::string DatabaseManagerStatus; DatabaseManagerStatus DB_MANAGER_WORKABLE = "WORKABLE"; @@ -35,7 +37,11 @@ CommSecureStore::set(CommSecureStore::userID, ""); CommSecureStore::set(CommSecureStore::deviceID, ""); CommSecureStore::set(CommSecureStore::commServicesAccessToken, ""); - SQLiteQueryExecutor::clearSensitiveData(); + + std::string backupDataKey = DatabaseManager::generateBackupDataKey(); + std::string backupLogDataKey = DatabaseManager::generateBackupLogDataKey(); + SQLiteQueryExecutor::clearSensitiveData(backupDataKey, backupLogDataKey); + PlatformSpecificTools::removeBackupDirectory(); CommMMKV::clearSensitiveData(); NotificationsCryptoModule::clearSensitiveData(); @@ -44,7 +50,7 @@ void DatabaseManager::initializeQueryExecutor(std::string &databasePath) { try { - SQLiteQueryExecutor::initialize(databasePath); + DatabaseManager::initializeSQLiteQueryExecutorProperties(databasePath); DatabaseManager::getQueryExecutor(); DatabaseManager::indicateQueryExecutorCreation(); Logger::log("Database manager initialized"); @@ -85,6 +91,53 @@ } } +std::string DatabaseManager::generateBackupDataKey() { + std::string backupDataKey = comm::crypto::Tools::generateRandomHexString( + SQLiteQueryExecutor::backupDataKeySize); + CommSecureStore::set(CommSecureStore::backupDataKey, backupDataKey); + return backupDataKey; +} + +std::string DatabaseManager::generateBackupLogDataKey() { + std::string backupLogDataKey = comm::crypto::Tools::generateRandomHexString( + SQLiteQueryExecutor::backupLogDataKeySize); + CommSecureStore::set(CommSecureStore::backupLogDataKey, backupLogDataKey); + return backupLogDataKey; +} + +void DatabaseManager::initializeSQLiteQueryExecutorProperties( + std::string &databasePath) { + std::call_once( + DatabaseManager::sqliteQueryExecutorPropertiesInitialized, + [&databasePath]() { + folly::Optional maybeBackupDataKey = + CommSecureStore::get(CommSecureStore::backupDataKey); + folly::Optional maybeBackupLogDataKey = + CommSecureStore::get(CommSecureStore::backupLogDataKey); + + std::string backupDataKey, backupLogDataKey; + + // In case of a non-existent database file, we always want to generate + // fresh key. + if (!SQLiteUtils::fileExists(databasePath) || !maybeBackupDataKey) { + backupDataKey = DatabaseManager::generateBackupDataKey(); + } else { + backupDataKey = maybeBackupDataKey.value(); + } + + // In case of a non-existent database file, we always want to generate + // fresh key. + if (!SQLiteUtils::fileExists(databasePath) || !maybeBackupLogDataKey) { + backupLogDataKey = DatabaseManager::generateBackupLogDataKey(); + } else { + backupLogDataKey = maybeBackupLogDataKey.value(); + } + + SQLiteQueryExecutor::initialize( + databasePath, backupDataKey, backupLogDataKey); + }); +} + bool DatabaseManager::checkIfDatabaseNeedsDeletion() { folly::Optional databaseManagerStatus = CommSecureStore::get(DATABASE_MANAGER_STATUS_KEY); diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h --- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h +++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h @@ -18,7 +18,6 @@ #include "entities/ThreadActivityEntry.h" #include "entities/UserInfo.h" -#include #include namespace comm { @@ -28,15 +27,10 @@ static sqlite3 *getConnection(); static void closeConnection(); - static std::once_flag initialized; - static int backupDataKeySize; static std::string backupLogDataKey; - static int backupLogDataKeySize; #ifndef EMSCRIPTEN static NativeSQLiteConnectionManager connectionManager; - static void generateBackupDataKey(); - static void generateBackupLogDataKey(); void cleanupDatabaseExceptAllowlist(sqlite3 *db) const; #else static WebSQLiteConnectionManager connectionManager; @@ -56,6 +50,10 @@ static std::string sqliteFilePath; static std::string backupDataKey; + // Constant key sizes + static int backupDataKeySize; + static int backupLogDataKeySize; + SQLiteQueryExecutor(); ~SQLiteQueryExecutor(); SQLiteQueryExecutor(std::string sqliteFilePath); @@ -214,8 +212,12 @@ getDMOperationsByType(const std::string &operationType) const override; #ifndef EMSCRIPTEN - static void clearSensitiveData(); - static void initialize(std::string &databasePath); + static void + clearSensitiveData(std::string &backupDataKey, std::string &backupLogDataKey); + static void initialize( + std::string &databasePath, + std::string &backupDataKey, + std::string &backupLogDataKey); void createMainCompaction(std::string backupID) const override; void captureBackupLogs() const override; void triggerBackupFileUpload() const override; diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp --- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp +++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp @@ -35,7 +35,6 @@ namespace comm { std::string SQLiteQueryExecutor::sqliteFilePath; -std::once_flag SQLiteQueryExecutor::initialized; std::string SQLiteQueryExecutor::backupDataKey; int SQLiteQueryExecutor::backupDataKeySize = 64; @@ -1656,8 +1655,10 @@ } #ifndef EMSCRIPTEN -void SQLiteQueryExecutor::clearSensitiveData() { - SQLiteQueryExecutor::closeConnection(); +void SQLiteQueryExecutor::clearSensitiveData( + std::string &backupDataKey, + std::string &backupLogDataKey) { + SQLiteQueryExecutor::connectionManager.closeConnection(); if (SQLiteUtils::fileExists(SQLiteQueryExecutor::sqliteFilePath) && std::remove(SQLiteQueryExecutor::sqliteFilePath.c_str())) { std::ostringstream errorStream; @@ -1666,35 +1667,18 @@ Logger::log(errorStream.str()); throw std::system_error(errno, std::generic_category(), errorStream.str()); } - SQLiteQueryExecutor::generateBackupDataKey(); - SQLiteQueryExecutor::generateBackupLogDataKey(); + SQLiteQueryExecutor::backupDataKey = backupDataKey; + SQLiteQueryExecutor::backupLogDataKey = backupLogDataKey; SQLiteQueryExecutor::migrate(); } -void SQLiteQueryExecutor::initialize(std::string &databasePath) { - std::call_once(SQLiteQueryExecutor::initialized, [&databasePath]() { - SQLiteQueryExecutor::sqliteFilePath = databasePath; - folly::Optional maybeBackupDataKey = - CommSecureStore::get(CommSecureStore::backupDataKey); - folly::Optional maybeBackupLogDataKey = - CommSecureStore::get(CommSecureStore::backupLogDataKey); - - // In case of a non-existent database file, we always want to generate fresh - // key. - if (!SQLiteUtils::fileExists(databasePath) || !maybeBackupDataKey) { - SQLiteQueryExecutor::generateBackupDataKey(); - } else { - SQLiteQueryExecutor::backupDataKey = maybeBackupDataKey.value(); - } - - // In case of a non-existent database file, we always want to generate fresh - // key. - if (!SQLiteUtils::fileExists(databasePath) || !maybeBackupLogDataKey) { - SQLiteQueryExecutor::generateBackupLogDataKey(); - } else { - SQLiteQueryExecutor::backupLogDataKey = maybeBackupLogDataKey.value(); - } - }); +void SQLiteQueryExecutor::initialize( + std::string &databasePath, + std::string &backupDataKey, + std::string &backupLogDataKey) { + SQLiteQueryExecutor::sqliteFilePath = databasePath; + SQLiteQueryExecutor::backupDataKey = backupDataKey; + SQLiteQueryExecutor::backupLogDataKey = backupLogDataKey; } void SQLiteQueryExecutor::cleanupDatabaseExceptAllowlist(sqlite3 *db) const { @@ -1819,20 +1803,6 @@ } } -void SQLiteQueryExecutor::generateBackupDataKey() { - std::string backupDataKey = comm::crypto::Tools::generateRandomHexString( - SQLiteQueryExecutor::backupDataKeySize); - CommSecureStore::set(CommSecureStore::backupDataKey, backupDataKey); - SQLiteQueryExecutor::backupDataKey = backupDataKey; -} - -void SQLiteQueryExecutor::generateBackupLogDataKey() { - std::string backupLogDataKey = comm::crypto::Tools::generateRandomHexString( - SQLiteQueryExecutor::backupLogDataKeySize); - CommSecureStore::set(CommSecureStore::backupLogDataKey, backupLogDataKey); - SQLiteQueryExecutor::backupLogDataKey = backupLogDataKey; -} - void SQLiteQueryExecutor::captureBackupLogs() const { if (!ServicesUtils::fullBackupSupport) { return;