diff --git a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp index e69704b49..863a85563 100644 --- a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp +++ b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp @@ -1,333 +1,346 @@ #include "NotificationsCryptoModule.h" #include "../../CryptoTools/Persist.h" #include "../../CryptoTools/Tools.h" #include "../../Tools/CommSecureStore.h" #include "../../Tools/PlatformSpecificTools.h" #include #include #include #include #include #include +#include #include namespace comm { const std::string NotificationsCryptoModule::secureStoreNotificationsAccountDataKey = "notificationsCryptoAccountDataKey"; const std::string NotificationsCryptoModule::notificationsCryptoAccountID = "notificationsCryptoAccountDataID"; const std::string NotificationsCryptoModule::keyserverHostedNotificationsID = "keyserverHostedNotificationsID"; const std::string NotificationsCryptoModule::initialEncryptedMessageContent = "{\"type\": \"init\"}"; const int NotificationsCryptoModule::olmEncryptedTypeMessage = 1; const int temporaryFilePathRandomSuffixLength = 32; -crypto::CryptoModule NotificationsCryptoModule::deserializeCryptoModule( +std::unique_ptr +NotificationsCryptoModule::deserializeCryptoModule( const std::string &path, const std::string &picklingKey) { std::ifstream pickledPersistStream(path, std::ifstream::in); if (!pickledPersistStream.good()) { throw std::runtime_error( "Attempt to deserialize non-existing notifications crypto account"); } std::stringstream pickledPersistStringStream; pickledPersistStringStream << pickledPersistStream.rdbuf(); pickledPersistStream.close(); std::string pickledPersist = pickledPersistStringStream.str(); folly::dynamic persistJSON; try { persistJSON = folly::parseJson(pickledPersist); } catch (const folly::json::parse_error &e) { throw std::runtime_error( "Notifications crypto account JSON deserialization failed with " "reason: " + std::string(e.what())); } std::string accountString = persistJSON["account"].asString(); crypto::OlmBuffer account = std::vector(accountString.begin(), accountString.end()); std::unordered_map sessions; if (persistJSON["sessions"].isNull()) { - return crypto::CryptoModule{ - notificationsCryptoAccountID, picklingKey, {account, sessions}}; + return std::make_unique( + notificationsCryptoAccountID, + picklingKey, + crypto::Persist({account, sessions})); } for (auto &sessionKeyValuePair : persistJSON["sessions"].items()) { std::string targetUserID = sessionKeyValuePair.first.asString(); std::string sessionData = sessionKeyValuePair.second.asString(); sessions[targetUserID] = std::vector(sessionData.begin(), sessionData.end()); } - return crypto::CryptoModule{ - notificationsCryptoAccountID, picklingKey, {account, sessions}}; + return std::make_unique( + notificationsCryptoAccountID, + picklingKey, + crypto::Persist({account, sessions})); } void NotificationsCryptoModule::serializeAndFlushCryptoModule( - crypto::CryptoModule &cryptoModule, + std::unique_ptr cryptoModule, const std::string &path, const std::string &picklingKey, const std::string &callingProcessName) { - crypto::Persist persist = cryptoModule.storeAsB64(picklingKey); + crypto::Persist persist = cryptoModule->storeAsB64(picklingKey); folly::dynamic sessions = folly::dynamic::object; for (auto &sessionKeyValuePair : persist.sessions) { std::string targetUserID = sessionKeyValuePair.first; crypto::OlmBuffer sessionData = sessionKeyValuePair.second; sessions[targetUserID] = std::string(sessionData.begin(), sessionData.end()); } std::string account = std::string(persist.account.begin(), persist.account.end()); folly::dynamic persistJSON = folly::dynamic::object("account", account)("sessions", sessions); std::string pickledPersist = folly::toJson(persistJSON); std::string temporaryFilePathRandomSuffix = crypto::Tools::generateRandomHexString( temporaryFilePathRandomSuffixLength); std::string temporaryPath = path + callingProcessName + temporaryFilePathRandomSuffix; mode_t readWritePermissionsMode = 0666; int temporaryFD = open(temporaryPath.c_str(), O_CREAT | O_WRONLY, readWritePermissionsMode); if (temporaryFD == -1) { throw std::runtime_error( "Failed to create temporary file. Unable to atomically update " "notifications crypto account. Details: " + std::string(strerror(errno))); } ssize_t bytesWritten = write(temporaryFD, pickledPersist.c_str(), pickledPersist.length()); if (bytesWritten == -1 || bytesWritten != pickledPersist.length()) { remove(temporaryPath.c_str()); throw std::runtime_error( "Failed to write all data to temporary file. Unable to atomically " "update notifications crypto account. Details: " + std::string(strerror(errno))); } if (fsync(temporaryFD) == -1) { remove(temporaryPath.c_str()); throw std::runtime_error( "Failed to synchronize temporary file data with hardware storage. " "Unable to atomically update notifications crypto account. Details: " + std::string(strerror(errno))); }; close(temporaryFD); if (rename(temporaryPath.c_str(), path.c_str()) == -1) { remove(temporaryPath.c_str()); throw std::runtime_error( "Failed to replace temporary file content with notifications crypto " "account. Unable to atomically update notifications crypto account. " "Details: " + std::string(strerror(errno))); } remove(temporaryPath.c_str()); } std::string NotificationsCryptoModule::getPicklingKey() { CommSecureStore secureStore{}; folly::Optional picklingKey = secureStore.get( NotificationsCryptoModule::secureStoreNotificationsAccountDataKey); if (!picklingKey.hasValue()) { throw std::runtime_error( "Attempt to retrieve notifications crypto account before it was " "correctly initialized."); } return picklingKey.value(); } void NotificationsCryptoModule::callCryptoModule( - std::function caller, + std::function< + void(const std::unique_ptr &cryptoModule)> caller, const std::string &callingProcessName) { const std::string picklingKey = NotificationsCryptoModule::getPicklingKey(); const std::string path = PlatformSpecificTools::getNotificationsCryptoAccountPath(); - crypto::CryptoModule cryptoModule = + std::unique_ptr cryptoModule = NotificationsCryptoModule::deserializeCryptoModule(path, picklingKey); caller(cryptoModule); NotificationsCryptoModule::serializeAndFlushCryptoModule( - cryptoModule, path, picklingKey, callingProcessName); + std::move(cryptoModule), path, picklingKey, callingProcessName); } void NotificationsCryptoModule::initializeNotificationsCryptoAccount( const std::string &callingProcessName) { const std::string notificationsCryptoAccountPath = PlatformSpecificTools::getNotificationsCryptoAccountPath(); std::ifstream notificationCryptoAccountCheck(notificationsCryptoAccountPath); if (notificationCryptoAccountCheck.good()) { // Implemented in CommmCoreModule semantics regarding public olm account // initialization is idempotent. We should follow the same approach when it // comes to notifications notificationCryptoAccountCheck.close(); return; } // There is no reason to check if the key is already present since if we are // in this place in the code we are about to create new account CommSecureStore secureStore{}; std::string picklingKey = crypto::Tools::generateRandomString(64); secureStore.set( NotificationsCryptoModule::secureStoreNotificationsAccountDataKey, picklingKey); - crypto::CryptoModule cryptoModule{ - NotificationsCryptoModule::notificationsCryptoAccountID}; + std::unique_ptr cryptoModule = + std::make_unique( + NotificationsCryptoModule::notificationsCryptoAccountID); NotificationsCryptoModule::serializeAndFlushCryptoModule( - cryptoModule, + std::move(cryptoModule), notificationsCryptoAccountPath, picklingKey, callingProcessName); } std::string NotificationsCryptoModule::getNotificationsIdentityKeys( const std::string &callingProcessName) { std::string identityKeys; - auto caller = [&identityKeys](crypto::CryptoModule cryptoModule) { - identityKeys = cryptoModule.getIdentityKeys(); + auto caller = [&identityKeys]( + const std::unique_ptr &cryptoModule) { + identityKeys = cryptoModule->getIdentityKeys(); }; NotificationsCryptoModule::callCryptoModule(caller, callingProcessName); return identityKeys; } std::string NotificationsCryptoModule::generateAndGetNotificationsPrekey( const std::string &callingProcessName) { std::string prekey; - auto caller = [&prekey](crypto::CryptoModule cryptoModule) { - prekey = cryptoModule.generateAndGetPrekey(); - }; + auto caller = + [&prekey](const std::unique_ptr &cryptoModule) { + prekey = cryptoModule->generateAndGetPrekey(); + }; NotificationsCryptoModule::callCryptoModule(caller, callingProcessName); return prekey; } std::string NotificationsCryptoModule::getNotificationsPrekeySignature( const std::string &callingProcessName) { std::string prekeySignature; - auto caller = [&prekeySignature](crypto::CryptoModule cryptoModule) { - prekeySignature = cryptoModule.getPrekeySignature(); + auto caller = [&prekeySignature]( + const std::unique_ptr &cryptoModule) { + prekeySignature = cryptoModule->getPrekeySignature(); }; NotificationsCryptoModule::callCryptoModule(caller, callingProcessName); return prekeySignature; } std::string NotificationsCryptoModule::getNotificationsOneTimeKeys( const size_t oneTimeKeysAmount, const std::string &callingProcessName) { std::string oneTimeKeys; - auto caller = [&oneTimeKeys, - oneTimeKeysAmount](crypto::CryptoModule cryptoModule) { - oneTimeKeys = cryptoModule.getOneTimeKeys(oneTimeKeysAmount); + auto caller = [&oneTimeKeys, oneTimeKeysAmount]( + const std::unique_ptr &cryptoModule) { + oneTimeKeys = cryptoModule->getOneTimeKeys(oneTimeKeysAmount); }; NotificationsCryptoModule::callCryptoModule(caller, callingProcessName); return oneTimeKeys; } crypto::EncryptedData NotificationsCryptoModule::initializeNotificationsSession( const std::string &identityKeys, const std::string &prekey, const std::string &prekeySignature, const std::string &oneTimeKeys, const std::string &callingProcessName) { crypto::EncryptedData initialEncryptedMessage; - auto caller = [&](crypto::CryptoModule &cryptoModule) { - cryptoModule.initializeOutboundForSendingSession( + auto caller = [&](const std::unique_ptr &cryptoModule) { + cryptoModule->initializeOutboundForSendingSession( NotificationsCryptoModule::keyserverHostedNotificationsID, std::vector(identityKeys.begin(), identityKeys.end()), std::vector(prekey.begin(), prekey.end()), std::vector(prekeySignature.begin(), prekeySignature.end()), std::vector(oneTimeKeys.begin(), oneTimeKeys.end())); - initialEncryptedMessage = cryptoModule.encrypt( + initialEncryptedMessage = cryptoModule->encrypt( NotificationsCryptoModule::keyserverHostedNotificationsID, NotificationsCryptoModule::initialEncryptedMessageContent); }; NotificationsCryptoModule::callCryptoModule(caller, callingProcessName); return initialEncryptedMessage; } bool NotificationsCryptoModule::isNotificationsSessionInitialized( const std::string &callingProcessName) { bool sessionInitialized; - auto caller = [&sessionInitialized](crypto::CryptoModule &cryptoModule) { - sessionInitialized = cryptoModule.hasSessionFor( + auto caller = [&sessionInitialized]( + const std::unique_ptr &cryptoModule) { + sessionInitialized = cryptoModule->hasSessionFor( NotificationsCryptoModule::keyserverHostedNotificationsID); }; NotificationsCryptoModule::callCryptoModule(caller, callingProcessName); return sessionInitialized; } void NotificationsCryptoModule::clearSensitiveData() { std::string notificationsCryptoAccountPath = PlatformSpecificTools::getNotificationsCryptoAccountPath(); if (remove(notificationsCryptoAccountPath.c_str()) == -1 && errno != ENOENT) { throw std::runtime_error( "Unable to remove notifications crypto account. Security requirements " "might be violated."); } } std::string NotificationsCryptoModule::decrypt( const std::string &data, const size_t messageType, const std::string &callingProcessName) { std::string decryptedData; - auto caller = [&](crypto::CryptoModule &cryptoModule) { + auto caller = [&](const std::unique_ptr &cryptoModule) { crypto::EncryptedData encryptedData{ std::vector(data.begin(), data.end()), messageType}; - decryptedData = cryptoModule.decrypt( + decryptedData = cryptoModule->decrypt( NotificationsCryptoModule::keyserverHostedNotificationsID, encryptedData); }; NotificationsCryptoModule::callCryptoModule(caller, callingProcessName); return decryptedData; } NotificationsCryptoModule::StatefulDecryptResult::StatefulDecryptResult( - crypto::CryptoModule cryptoModule, + std::unique_ptr cryptoModule, std::string decryptedData) - : cryptoModuleState(std::make_unique(cryptoModule)), - decryptedData(decryptedData) { + : cryptoModuleState(std::move(cryptoModule)), decryptedData(decryptedData) { } std::string NotificationsCryptoModule::StatefulDecryptResult::getDecryptedData() { return this->decryptedData; } std::unique_ptr NotificationsCryptoModule::statefulDecrypt( const std::string &data, const size_t messageType) { std::string path = PlatformSpecificTools::getNotificationsCryptoAccountPath(); std::string picklingKey = NotificationsCryptoModule::getPicklingKey(); - crypto::CryptoModule cryptoModule = + std::unique_ptr cryptoModule = NotificationsCryptoModule::deserializeCryptoModule(path, picklingKey); crypto::EncryptedData encryptedData{ std::vector(data.begin(), data.end()), messageType}; - std::string decryptedData = cryptoModule.decrypt( + std::string decryptedData = cryptoModule->decrypt( NotificationsCryptoModule::keyserverHostedNotificationsID, encryptedData); - StatefulDecryptResult statefulDecryptResult(cryptoModule, decryptedData); + StatefulDecryptResult statefulDecryptResult( + std::move(cryptoModule), decryptedData); return std::make_unique( std::move(statefulDecryptResult)); } void NotificationsCryptoModule::flushState( std::unique_ptr statefulDecryptResult, const std::string &callingProcessName) { std::string path = PlatformSpecificTools::getNotificationsCryptoAccountPath(); std::string picklingKey = NotificationsCryptoModule::getPicklingKey(); - crypto::CryptoModule cryptoModule = *statefulDecryptResult->cryptoModuleState; - NotificationsCryptoModule::serializeAndFlushCryptoModule( - cryptoModule, path, picklingKey, callingProcessName); + std::move(statefulDecryptResult->cryptoModuleState), + path, + picklingKey, + callingProcessName); } } // namespace comm diff --git a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.h b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.h index 2f9a557ae..85211a217 100644 --- a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.h +++ b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.h @@ -1,72 +1,73 @@ #pragma once #include "../../CryptoTools/CryptoModule.h" #include namespace comm { class NotificationsCryptoModule { const static std::string secureStoreNotificationsAccountDataKey; const static std::string notificationsCryptoAccountID; const static std::string keyserverHostedNotificationsID; const static std::string initialEncryptedMessageContent; static std::string getPicklingKey(); static void serializeAndFlushCryptoModule( - crypto::CryptoModule &cryptoModule, + std::unique_ptr cryptoModule, const std::string &path, const std::string &picklingKey, const std::string &callingProcessName); - static crypto::CryptoModule deserializeCryptoModule( + static std::unique_ptr deserializeCryptoModule( const std::string &path, const std::string &picklingKey); static void callCryptoModule( - std::function caller, + std::function &cryptoModule)> caller, const std::string &callingProcessName); public: const static int olmEncryptedTypeMessage; static void initializeNotificationsCryptoAccount(const std::string &callingProcessName); static void clearSensitiveData(); static std::string getNotificationsIdentityKeys(const std::string &callingProcessName); static std::string generateAndGetNotificationsPrekey(const std::string &callingProcessName); static std::string getNotificationsPrekeySignature(const std::string &callingProcessName); static std::string getNotificationsOneTimeKeys( const size_t oneTimeKeysAmount, const std::string &callingProcessName); static crypto::EncryptedData initializeNotificationsSession( const std::string &identityKeys, const std::string &prekey, const std::string &prekeySignature, const std::string &oneTimeKeys, const std::string &callingProcessName); static bool isNotificationsSessionInitialized(const std::string &callingProcessName); static std::string decrypt( const std::string &data, const size_t messageType, const std::string &callingProcessName); class StatefulDecryptResult { StatefulDecryptResult( - crypto::CryptoModule cryptoModule, + std::unique_ptr cryptoModule, std::string decryptedData); std::unique_ptr cryptoModuleState; std::string decryptedData; friend NotificationsCryptoModule; public: std::string getDecryptedData(); }; static std::unique_ptr statefulDecrypt(const std::string &data, const size_t messageType); static void flushState( std::unique_ptr statefulDecryptResult, const std::string &callingProcessName); }; } // namespace comm