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 @@ -11,6 +11,7 @@ const static std::string keyserverHostedNotificationsID; const static std::string initialEncryptedMessageContent; + static std::string getPicklingKey(); static void serializeAndFlushCryptoModule( crypto::CryptoModule &cryptoModule, const std::string &path, @@ -49,5 +50,23 @@ const std::string &data, const size_t messageType, const std::string &callingProcessName); + + class StatefulDecryptResult { + StatefulDecryptResult( + crypto::CryptoModule 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 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 @@ -132,9 +132,7 @@ remove(temporaryPath.c_str()); } -void NotificationsCryptoModule::callCryptoModule( - std::function caller, - const std::string &callingProcessName) { +std::string NotificationsCryptoModule::getPicklingKey() { CommSecureStore secureStore{}; folly::Optional picklingKey = secureStore.get( NotificationsCryptoModule::secureStoreNotificationsAccountDataKey); @@ -143,15 +141,20 @@ "Attempt to retrieve notifications crypto account before it was " "correctly initialized."); } + return picklingKey.value(); +} +void NotificationsCryptoModule::callCryptoModule( + std::function caller, + const std::string &callingProcessName) { + const std::string picklingKey = NotificationsCryptoModule::getPicklingKey(); const std::string path = PlatformSpecificTools::getNotificationsCryptoAccountPath(); crypto::CryptoModule cryptoModule = - NotificationsCryptoModule::deserializeCryptoModule( - path, picklingKey.value()); + NotificationsCryptoModule::deserializeCryptoModule(path, picklingKey); caller(cryptoModule); NotificationsCryptoModule::serializeAndFlushCryptoModule( - cryptoModule, path, picklingKey.value(), callingProcessName); + cryptoModule, path, picklingKey, callingProcessName); } void NotificationsCryptoModule::initializeNotificationsCryptoAccount( @@ -283,4 +286,48 @@ NotificationsCryptoModule::callCryptoModule(caller, callingProcessName); return decryptedData; } + +NotificationsCryptoModule::StatefulDecryptResult::StatefulDecryptResult( + crypto::CryptoModule cryptoModule, + std::string decryptedData) + : cryptoModuleState(std::make_unique(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 = + NotificationsCryptoModule::deserializeCryptoModule(path, picklingKey); + crypto::EncryptedData encryptedData{ + std::vector(data.begin(), data.end()), messageType}; + std::string decryptedData = cryptoModule.decrypt( + NotificationsCryptoModule::keyserverHostedNotificationsID, encryptedData); + StatefulDecryptResult statefulDecryptResult(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); +} } // 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 @@ -63,6 +63,10 @@ UNNotificationContent *publicUserContent = content; // Step 1: notification decryption. + std::unique_ptr + statefulDecryptResultPtr; + BOOL decryptionExecuted = NO; + if ([self shouldBeDecrypted:content.userInfo]) { std::optional notifID; NSString *objcNotifID = content.userInfo[@"id"]; @@ -73,7 +77,8 @@ std::string decryptErrorMessage; try { @try { - [self decryptContentInPlace:content]; + statefulDecryptResultPtr = [self decryptContentInPlace:content]; + decryptionExecuted = YES; } @catch (NSException *e) { decryptErrorMessage = "NSE: Received Obj-C exception: " + std::string([e.name UTF8String]) + @@ -215,8 +220,14 @@ withPublicUserContent:publicUserContent]; return; } + [self callContentHandlerForKey:contentHandlerKey withContent:publicUserContent]; + + if (decryptionExecuted) { + comm::NotificationsCryptoModule::flushState( + std::move(statefulDecryptResultPtr), callingProcessName); + } } - (void)serviceExtensionTimeWillExpire { @@ -456,20 +467,17 @@ [payload[encryptionFailureKey] isEqualToNumber:@(1)]; } -- (NSString *)singleDecrypt:(NSString *)data { - std::string encryptedData = std::string([data UTF8String]); - return [NSString - stringWithUTF8String: - (comm::NotificationsCryptoModule::decrypt( - encryptedData, - comm::NotificationsCryptoModule::olmEncryptedTypeMessage, - callingProcessName)) - .c_str()]; -} +- (std::unique_ptr) + decryptContentInPlace:(UNMutableNotificationContent *)content { + std::string encryptedData = + std::string([content.userInfo[encryptedPayloadKey] UTF8String]); + + auto decryptResult = comm::NotificationsCryptoModule::statefulDecrypt( + encryptedData, comm::NotificationsCryptoModule::olmEncryptedTypeMessage); -- (void)decryptContentInPlace:(UNMutableNotificationContent *)content { NSString *decryptedSerializedPayload = - [self singleDecrypt:content.userInfo[encryptedPayloadKey]]; + [NSString stringWithUTF8String:decryptResult->getDecryptedData().c_str()]; + NSDictionary *decryptedPayload = [NSJSONSerialization JSONObjectWithData:[decryptedSerializedPayload dataUsingEncoding:NSUTF8StringEncoding] @@ -526,6 +534,8 @@ [mutableUserInfo removeObjectForKey:encryptedPayloadKey]; mutableUserInfo[@"successfullyDecrypted"] = @(YES); content.userInfo = mutableUserInfo; + + return decryptResult; } // Apple documentation for NSE does not explicitly state