diff --git a/native/android/app/src/cpp/NotificationsCryptoModuleJNIHelper.cpp b/native/android/app/src/cpp/NotificationsCryptoModuleJNIHelper.cpp --- a/native/android/app/src/cpp/NotificationsCryptoModuleJNIHelper.cpp +++ b/native/android/app/src/cpp/NotificationsCryptoModuleJNIHelper.cpp @@ -9,11 +9,11 @@ std::string NotificationsCryptoModuleJNIHelper::decrypt( facebook::jni::alias_ref jThis, + std::string keyserverID, std::string data, - int messageType, - std::string callingProcessName) { + int messageType) { std::string decryptedData = - NotificationsCryptoModule::decrypt(data, messageType, callingProcessName); + NotificationsCryptoModule::decrypt(keyserverID, data, messageType); return decryptedData; } @@ -25,4 +25,4 @@ makeNativeMethod("decrypt", NotificationsCryptoModuleJNIHelper::decrypt), }); } -} // namespace comm \ No newline at end of file +} // namespace comm diff --git a/native/android/app/src/main/java/app/comm/android/fbjni/NotificationsCryptoModule.java b/native/android/app/src/main/java/app/comm/android/fbjni/NotificationsCryptoModule.java --- a/native/android/app/src/main/java/app/comm/android/fbjni/NotificationsCryptoModule.java +++ b/native/android/app/src/main/java/app/comm/android/fbjni/NotificationsCryptoModule.java @@ -3,5 +3,5 @@ public class NotificationsCryptoModule { public static native int olmEncryptedTypeMessage(); public static native String - decrypt(String data, int messageType, String callingProcessName); + decrypt(String keyserverID, String data, int messageType); } diff --git a/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java b/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java --- a/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java +++ b/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java @@ -91,9 +91,19 @@ @Override public void onMessageReceived(RemoteMessage message) { + if (message.getData().get(KEYSERVER_ID_KEY) == null) { + displayErrorMessageNotification( + "Received notification without keyserver ID.", + "Missing keyserver ID.", + null); + return; + } + + String senderKeyserverID = message.getData().get(KEYSERVER_ID_KEY); + if (message.getData().get(ENCRYPTED_PAYLOAD_KEY) != null) { try { - message = this.decryptRemoteMessage(message); + message = this.decryptRemoteMessage(message, senderKeyserverID); } catch (JSONException e) { Log.w("COMM", "Malformed notification JSON payload.", e); return; @@ -377,14 +387,15 @@ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); } - private RemoteMessage decryptRemoteMessage(RemoteMessage message) + private RemoteMessage + decryptRemoteMessage(RemoteMessage message, String senderKeyserverID) throws JSONException, IllegalStateException { String encryptedSerializedPayload = message.getData().get(ENCRYPTED_PAYLOAD_KEY); String decryptedSerializedPayload = NotificationsCryptoModule.decrypt( + senderKeyserverID, encryptedSerializedPayload, - NotificationsCryptoModule.olmEncryptedTypeMessage(), - "CommNotificationsHandler"); + NotificationsCryptoModule.olmEncryptedTypeMessage()); JSONObject decryptedPayload = new JSONObject(decryptedSerializedPayload); 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 @@ -12,6 +12,8 @@ #include #ifndef EMSCRIPTEN +#include "../CryptoTools/CryptoModule.h" +#include "../Notifications/BackgroundDataStorage/NotificationsCryptoModule.h" #include "CommSecureStore.h" #include "PlatformSpecificTools.h" #include "StaffUtils.h" @@ -541,6 +543,41 @@ return create_table(db, query, "integrity_store"); } +bool migrate_notifs_crypto_account(sqlite3 *db) { +#ifndef EMSCRIPTEN + std::string legacyCryptoAccountDataKey = "cryptoAccountDataKey"; + folly::Optional secretKey = + CommSecureStore::get(legacyCryptoAccountDataKey); + + if (!secretKey.hasValue()) { + return false; + } + + std::unique_ptr legacyNotifsAccount = + NotificationsCryptoModule::migrateLegacyNotificationsCryptoModule(); + + if (!legacyNotifsAccount) { + return true; + } + + std::string insert_notifs_account_query = + "REPLACE INTO olm_persist_account (id, account_data) " + "VALUES (?, ?);"; + + crypto::Persist legacyNotifsPersist = + legacyNotifsAccount->storeAsB64(secretKey.value()); + std::string notifsAccountData = std::string( + legacyNotifsPersist.account.begin(), legacyNotifsPersist.account.end()); + + replaceEntity( + db, insert_notifs_account_query, {NOTIFS_ACCOUNT_ID, notifsAccountData}); + + return true; +#else + return true; +#endif +} + bool create_schema(sqlite3 *db) { char *error; sqlite3_exec( @@ -895,7 +932,8 @@ {34, {enable_rollback_journal_mode, false}}, {35, {create_communities_table, true}}, {36, {create_messages_to_device_table, true}}, - {37, {create_integrity_table, true}}}}; + {37, {create_integrity_table, true}}, + {38, {migrate_notifs_crypto_account, true}}}}; enum class MigrationResult { SUCCESS, FAILURE, NOT_APPLIED }; diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp --- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp +++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp @@ -906,21 +906,35 @@ auto prekeyCpp{prekey.utf8(rt)}; auto prekeySignatureCpp{prekeySignature.utf8(rt)}; auto oneTimeKeyCpp{oneTimeKey.utf8(rt)}; + auto keyserverIDCpp{keyserverID.utf8(rt)}; return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; crypto::EncryptedData result; try { - // Introduced temporarily to make this diff non-breaking change - NotificationsCryptoModule::initializeNotificationsCryptoAccount( - "Comm"); - result = NotificationsCryptoModule::initializeNotificationsSession( - identityKeysCpp, - prekeyCpp, - prekeySignatureCpp, - oneTimeKeyCpp, - "Comm"); + this->notifsCryptoModule->initializeOutboundForSendingSession( + keyserverIDCpp, + std::vector( + identityKeysCpp.begin(), identityKeysCpp.end()), + std::vector(prekeyCpp.begin(), prekeyCpp.end()), + std::vector( + prekeySignatureCpp.begin(), prekeySignatureCpp.end()), + std::vector( + oneTimeKeyCpp.begin(), oneTimeKeyCpp.end())); + + result = this->notifsCryptoModule->encrypt( + keyserverIDCpp, + NotificationsCryptoModule::initialEncryptedMessageContent); + + std::shared_ptr keyserverNotificationsSession = + this->notifsCryptoModule->getSessionByDeviceId(keyserverIDCpp); + + NotificationsCryptoModule::persistNotificationsSession( + keyserverIDCpp, keyserverNotificationsSession); + + this->notifsCryptoModule->removeSessionByDeviceId(keyserverIDCpp); + this->persistCryptoModules(false, true); } catch (const std::exception &e) { error = e.what(); } diff --git a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.h b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.h --- a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.h +++ b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.h @@ -7,59 +7,65 @@ 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( - std::unique_ptr cryptoModule, - const std::string &path, - const std::string &picklingKey, - const std::string &callingProcessName); + // Used for migration of legacy notifications sessions static std::unique_ptr deserializeCryptoModule( const std::string &path, const std::string &picklingKey); - static void callCryptoModule( - std::function &cryptoModule)> caller, - const std::string &callingProcessName); + static std::string + getKeyserverNotificationsSessionKey(const std::string &keyserverID); + static std::string serializeNotificationsSession( + std::shared_ptr session, + std::string picklingKey); + static std::pair, std::string> + deserializeNotificationsSession(const std::string &serializedSession); + static void persistNotificationsSessionInternal( + const std::string &keyserverID, + const std::string &picklingKey, + std::shared_ptr session); + static std::pair, std::string> + fetchNotificationsSession(const std::string &keyserverID); public: + const static std::string initialEncryptedMessageContent; const static int olmEncryptedTypeMessage; - static void - initializeNotificationsCryptoAccount(const std::string &callingProcessName); + + static std::unique_ptr + migrateLegacyNotificationsCryptoModule(); static void clearSensitiveData(); - 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 void persistNotificationsSession( + const std::string &keyserverID, + std::shared_ptr keyserverNotificationsSession); + static bool isNotificationsSessionInitialized(const std::string &keyserverID); static std::string decrypt( + const std::string &keyserverID, const std::string &data, - const size_t messageType, - const std::string &callingProcessName); + const size_t messageType); class StatefulDecryptResult { StatefulDecryptResult( - std::unique_ptr cryptoModule, + std::unique_ptr session, + std::string keyserverID, + std::string picklingKey, std::string decryptedData); - std::unique_ptr cryptoModuleState; + std::unique_ptr sessionState; + std::string keyserverID; + std::string picklingKey; std::string decryptedData; friend NotificationsCryptoModule; public: std::string getDecryptedData(); + std::string getKeyserverID(); + std::string getPicklingKey(); }; - static std::unique_ptr - statefulDecrypt(const std::string &data, const size_t messageType); - static void flushState( - std::unique_ptr statefulDecryptResult, - const std::string &callingProcessName); + static std::unique_ptr statefulDecrypt( + const std::string &keyserverID, + const std::string &data, + const size_t messageType); + static void + flushState(std::unique_ptr statefulDecryptResult); }; } // namespace comm diff --git a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp --- a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp +++ b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp @@ -1,31 +1,34 @@ #include "NotificationsCryptoModule.h" #include "../../CryptoTools/Persist.h" #include "../../CryptoTools/Tools.h" +#include "../../Tools/CommMMKV.h" #include "../../Tools/CommSecureStore.h" +#include "../../Tools/Logger.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; + +// This constant is only used to migrate the existing notifications +// session with production keyserver from flat file to MMKV. This +// migration will fire when user updates the app. It will also fire +// on dev env provided old keyserver set up is used. Developers willing +// to use new keyserver set up must log out before installing updated +// app version. Do not introduce new usages of this constant in the code!!! +const std::string ashoatKeyserverIDUsedOnlyForMigrationFromLegacyNotifStorage = + "256"; std::unique_ptr NotificationsCryptoModule::deserializeCryptoModule( @@ -74,156 +77,77 @@ crypto::Persist({account, sessions})); } -void NotificationsCryptoModule::serializeAndFlushCryptoModule( - std::unique_ptr cryptoModule, - const std::string &path, - const std::string &picklingKey, - const std::string &callingProcessName) { - 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 NotificationsCryptoModule::getKeyserverNotificationsSessionKey( + const std::string &keyserverID) { + return "KEYSERVER." + keyserverID + ".NOTIFS_SESSION"; +} - 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::serializeNotificationsSession( + std::shared_ptr session, + std::string picklingKey) { + crypto::OlmBuffer pickledSessionBytes = session->storeAsB64(picklingKey); + std::string pickledSession = + std::string{pickledSessionBytes.begin(), pickledSessionBytes.end()}; + folly::dynamic serializedSessionJson = folly::dynamic::object( + "session", pickledSession)("picklingKey", picklingKey); + return folly::toJson(serializedSessionJson); } -std::string NotificationsCryptoModule::getPicklingKey() { - folly::Optional picklingKey = CommSecureStore::get( - NotificationsCryptoModule::secureStoreNotificationsAccountDataKey); - if (!picklingKey.hasValue()) { +std::pair, std::string> +NotificationsCryptoModule::deserializeNotificationsSession( + const std::string &serializedSession) { + folly::dynamic serializedSessionJson; + try { + serializedSessionJson = folly::parseJson(serializedSession); + } catch (const folly::json::parse_error &e) { throw std::runtime_error( - "Attempt to retrieve notifications crypto account before it was " - "correctly initialized."); + "Notifications session deserialization failed with reason: " + + std::string(e.what())); } - return picklingKey.value(); -} -void NotificationsCryptoModule::callCryptoModule( - 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(); - std::unique_ptr cryptoModule = - NotificationsCryptoModule::deserializeCryptoModule(path, picklingKey); - caller(cryptoModule); - NotificationsCryptoModule::serializeAndFlushCryptoModule( - std::move(cryptoModule), path, picklingKey, callingProcessName); + std::string pickledSession = serializedSessionJson["session"].asString(); + crypto::OlmBuffer pickledSessionBytes = + crypto::OlmBuffer{pickledSession.begin(), pickledSession.end()}; + std::string picklingKey = serializedSessionJson["picklingKey"].asString(); + std::unique_ptr session = + crypto::Session::restoreFromB64(picklingKey, pickledSessionBytes); + return {std::move(session), picklingKey}; } -void NotificationsCryptoModule::initializeNotificationsCryptoAccount( - const std::string &callingProcessName) { +std::unique_ptr +NotificationsCryptoModule::migrateLegacyNotificationsCryptoModule() { 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 + + if (!notificationCryptoAccountCheck.good()) { notificationCryptoAccountCheck.close(); - return; + return nullptr; } - // 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 - std::string picklingKey = crypto::Tools::generateRandomString(64); - CommSecureStore::set( - NotificationsCryptoModule::secureStoreNotificationsAccountDataKey, - picklingKey); - - std::unique_ptr cryptoModule = - std::make_unique( - NotificationsCryptoModule::notificationsCryptoAccountID); - NotificationsCryptoModule::serializeAndFlushCryptoModule( - std::move(cryptoModule), - notificationsCryptoAccountPath, - picklingKey, - callingProcessName); -} + notificationCryptoAccountCheck.close(); -crypto::EncryptedData NotificationsCryptoModule::initializeNotificationsSession( - const std::string &identityKeys, - const std::string &prekey, - const std::string &prekeySignature, - const std::string &oneTimeKey, - const std::string &callingProcessName) { - crypto::EncryptedData initialEncryptedMessage; - 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(oneTimeKey.begin(), oneTimeKey.end())); - initialEncryptedMessage = cryptoModule->encrypt( - NotificationsCryptoModule::keyserverHostedNotificationsID, - NotificationsCryptoModule::initialEncryptedMessageContent); - }; - NotificationsCryptoModule::callCryptoModule(caller, callingProcessName); - return initialEncryptedMessage; -} + std::string legacySecureStoreNotifsAccountKey = + "notificationsCryptoAccountDataKey"; + folly::Optional legacyPicklingKey = + CommSecureStore::get(legacySecureStoreNotifsAccountKey); + if (!legacyPicklingKey.hasValue()) { + throw std::runtime_error( + "Attempt to migrate legacy notifications account but pickling key " + "missing."); + } -bool NotificationsCryptoModule::isNotificationsSessionInitialized( - const std::string &callingProcessName) { - bool sessionInitialized; - auto caller = [&sessionInitialized]( - const std::unique_ptr &cryptoModule) { - sessionInitialized = cryptoModule->hasSessionFor( - NotificationsCryptoModule::keyserverHostedNotificationsID); - }; - NotificationsCryptoModule::callCryptoModule(caller, callingProcessName); - return sessionInitialized; + std::unique_ptr legacyCryptoModule = + NotificationsCryptoModule::deserializeCryptoModule( + notificationsCryptoAccountPath, legacyPicklingKey.value()); + + std::string legacyNotificationsSessionID = "keyserverHostedNotificationsID"; + std::shared_ptr legacyNotificationsSession = + legacyCryptoModule->getSessionByDeviceId(legacyNotificationsSessionID); + + NotificationsCryptoModule::persistNotificationsSession( + ashoatKeyserverIDUsedOnlyForMigrationFromLegacyNotifStorage, + legacyNotificationsSession); + return legacyCryptoModule; } void NotificationsCryptoModule::clearSensitiveData() { @@ -236,26 +160,86 @@ } } +void NotificationsCryptoModule::persistNotificationsSessionInternal( + const std::string &keyserverID, + const std::string &picklingKey, + std::shared_ptr session) { + std::string serializedSession = + NotificationsCryptoModule::serializeNotificationsSession( + session, picklingKey); + std::string keyserverNotificationsSessionKey = + NotificationsCryptoModule::getKeyserverNotificationsSessionKey( + keyserverID); + + bool sessionStored = + CommMMKV::setString(keyserverNotificationsSessionKey, serializedSession); + + if (!sessionStored) { + throw std::runtime_error( + "Failed to persist to MMKV notifications session for keyserver: " + + keyserverID); + } +} + +std::pair, std::string> +NotificationsCryptoModule::fetchNotificationsSession( + const std::string &keyserverID) { + std::string keyserverNotificationsSessionKey = + NotificationsCryptoModule::getKeyserverNotificationsSessionKey( + keyserverID); + std::optional serializedSession = + CommMMKV::getString(keyserverNotificationsSessionKey); + + if (!serializedSession.has_value()) { + throw std::runtime_error( + "Missing notifications session for keyserver: " + keyserverID); + } + + return NotificationsCryptoModule::deserializeNotificationsSession( + serializedSession.value()); +} + +void NotificationsCryptoModule::persistNotificationsSession( + const std::string &keyserverID, + std::shared_ptr keyserverNotificationsSession) { + std::string picklingKey = crypto::Tools::generateRandomString(64); + NotificationsCryptoModule::persistNotificationsSessionInternal( + keyserverID, picklingKey, keyserverNotificationsSession); +} + +bool NotificationsCryptoModule::isNotificationsSessionInitialized( + const std::string &keyserverID) { + std::string keyserverNotificationsSessionKey = + "KEYSERVER." + keyserverID + ".NOTIFS_SESSION"; + return CommMMKV::getString(keyserverNotificationsSessionKey).has_value(); +} + std::string NotificationsCryptoModule::decrypt( + const std::string &keyserverID, const std::string &data, - const size_t messageType, - const std::string &callingProcessName) { - std::string decryptedData; - auto caller = [&](const std::unique_ptr &cryptoModule) { - crypto::EncryptedData encryptedData{ - std::vector(data.begin(), data.end()), messageType}; - decryptedData = cryptoModule->decrypt( - NotificationsCryptoModule::keyserverHostedNotificationsID, - encryptedData); - }; - NotificationsCryptoModule::callCryptoModule(caller, callingProcessName); + const size_t messageType) { + std::unique_ptr session; + std::string picklingKey; + std::tie(session, picklingKey) = + NotificationsCryptoModule::fetchNotificationsSession(keyserverID); + + crypto::EncryptedData encryptedData{ + std::vector(data.begin(), data.end()), messageType}; + std::string decryptedData = session->decrypt(encryptedData); + NotificationsCryptoModule::persistNotificationsSessionInternal( + keyserverID, picklingKey, std::move(session)); return decryptedData; } NotificationsCryptoModule::StatefulDecryptResult::StatefulDecryptResult( - std::unique_ptr cryptoModule, + std::unique_ptr session, + std::string keyserverID, + std::string picklingKey, std::string decryptedData) - : cryptoModuleState(std::move(cryptoModule)), decryptedData(decryptedData) { + : sessionState(std::move(session)), + keyserverID(keyserverID), + picklingKey(picklingKey), + decryptedData(decryptedData) { } std::string @@ -263,37 +247,39 @@ return this->decryptedData; } +std::string NotificationsCryptoModule::StatefulDecryptResult::getKeyserverID() { + return this->keyserverID; +} + +std::string NotificationsCryptoModule::StatefulDecryptResult::getPicklingKey() { + return this->picklingKey; +} + std::unique_ptr NotificationsCryptoModule::statefulDecrypt( + const std::string &keyserverID, const std::string &data, const size_t messageType) { - std::string path = PlatformSpecificTools::getNotificationsCryptoAccountPath(); - std::string picklingKey = NotificationsCryptoModule::getPicklingKey(); + std::unique_ptr session; + std::string picklingKey; + std::tie(session, picklingKey) = + NotificationsCryptoModule::fetchNotificationsSession(keyserverID); - std::unique_ptr cryptoModule = - NotificationsCryptoModule::deserializeCryptoModule(path, picklingKey); crypto::EncryptedData encryptedData{ std::vector(data.begin(), data.end()), messageType}; - std::string decryptedData = cryptoModule->decrypt( - NotificationsCryptoModule::keyserverHostedNotificationsID, encryptedData); - StatefulDecryptResult statefulDecryptResult( - std::move(cryptoModule), decryptedData); + std::string decryptedData = session->decrypt(encryptedData); + StatefulDecryptResult statefulDecryptResult( + std::move(session), keyserverID, picklingKey, 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(); - - NotificationsCryptoModule::serializeAndFlushCryptoModule( - std::move(statefulDecryptResult->cryptoModuleState), - path, - picklingKey, - callingProcessName); + std::unique_ptr statefulDecryptResult) { + NotificationsCryptoModule::persistNotificationsSessionInternal( + statefulDecryptResult->getKeyserverID(), + statefulDecryptResult->getPicklingKey(), + std::move(statefulDecryptResult->sessionState)); } } // namespace comm diff --git a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModuleJNIHelper.h b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModuleJNIHelper.h --- a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModuleJNIHelper.h +++ b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModuleJNIHelper.h @@ -14,10 +14,10 @@ static std::string decrypt( facebook::jni::alias_ref jThis, + std::string keyserverID, std::string data, - int messageType, - std::string callingProcessName); + int messageType); static void registerNatives(); }; -} // namespace comm \ No newline at end of file +} // namespace comm diff --git a/native/ios/NotificationService/NotificationService.mm b/native/ios/NotificationService/NotificationService.mm --- a/native/ios/NotificationService/NotificationService.mm +++ b/native/ios/NotificationService/NotificationService.mm @@ -20,7 +20,7 @@ const std::string mmkvKeySeparator = "."; const std::string mmkvKeyserverPrefix = "KEYSERVER"; const std::string mmkvUnreadCountSuffix = "UNREAD_COUNT"; -const std::string callingProcessName = "NSE"; + // The context for this constant can be found here: // https://linear.app/comm/issue/ENG-3074#comment-bd2f5e28 int64_t const notificationRemovalDelay = (int64_t)(0.1 * NSEC_PER_SEC); @@ -279,7 +279,7 @@ if (decryptionExecuted) { comm::NotificationsCryptoModule::flushState( - std::move(statefulDecryptResultPtr), callingProcessName); + std::move(statefulDecryptResultPtr)); } } @@ -549,8 +549,17 @@ std::string encryptedData = std::string([content.userInfo[encryptedPayloadKey] UTF8String]); + if (!content.userInfo[keyserverIDKey]) { + throw std::runtime_error( + "Received encrypted notification without keyserverID."); + } + std::string senderKeyserverID = + std::string([content.userInfo[keyserverIDKey] UTF8String]); + auto decryptResult = comm::NotificationsCryptoModule::statefulDecrypt( - encryptedData, comm::NotificationsCryptoModule::olmEncryptedTypeMessage); + senderKeyserverID, + encryptedData, + comm::NotificationsCryptoModule::olmEncryptedTypeMessage); NSString *decryptedSerializedPayload = [NSString stringWithUTF8String:decryptResult->getDecryptedData().c_str()]; diff --git a/web/shared-worker/_generated/comm_query_executor.wasm b/web/shared-worker/_generated/comm_query_executor.wasm index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@