diff --git a/native/ios/Comm.xcodeproj/project.pbxproj b/native/ios/Comm.xcodeproj/project.pbxproj --- a/native/ios/Comm.xcodeproj/project.pbxproj +++ b/native/ios/Comm.xcodeproj/project.pbxproj @@ -56,7 +56,11 @@ 8EF7756E2A7513F40046A385 /* MessageStore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8EF7756D2A7513F40046A385 /* MessageStore.cpp */; }; 8EF775712A751B780046A385 /* ReportStore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8EF7756F2A751B780046A385 /* ReportStore.cpp */; }; B71AFF1F265EDD8600B22352 /* IBMPlexSans-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B71AFF1E265EDD8600B22352 /* IBMPlexSans-Medium.ttf */; }; + CB1090982AA0C2E100DB02FC /* StaffUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB1090962AA0C2E000DB02FC /* StaffUtils.cpp */; }; + CB1090992AA0C35200DB02FC /* StaffUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB1090962AA0C2E000DB02FC /* StaffUtils.cpp */; }; CB1648AF27CFBE6A00394D9D /* CryptoModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71BF5B7B26BBDA6100EDE27D /* CryptoModule.cpp */; }; + CB1F33182AA88D5700CDE491 /* CommIOSNotificationsBlobClient.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB1F33172AA88D5700CDE491 /* CommIOSNotificationsBlobClient.mm */; }; + CB1F33192AA88D7900CDE491 /* CommIOSNotificationsBlobClient.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB1F33172AA88D5700CDE491 /* CommIOSNotificationsBlobClient.mm */; }; CB24361829A39A2500FEC4E1 /* NotificationsCryptoModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB24361729A39A2500FEC4E1 /* NotificationsCryptoModule.cpp */; }; CB2689002A2DF58000EC7300 /* CommConstants.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB2688FF2A2DF56000EC7300 /* CommConstants.cpp */; }; CB38B48228771C7A00171182 /* NonBlockingLock.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB38B47B287718A200171182 /* NonBlockingLock.mm */; }; @@ -77,8 +81,6 @@ CB7EF17E295C674300B17035 /* CommIOSNotifications.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB7EF17D295C5D1800B17035 /* CommIOSNotifications.mm */; }; CB7EF180295C674300B17035 /* CommIOSNotificationsBridgeQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB7EF17B295C580500B17035 /* CommIOSNotificationsBridgeQueue.mm */; }; CB90951F29534B32002F2A7F /* CommSecureStore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 71D4D7CB26C50B1000FCDBCD /* CommSecureStore.mm */; }; - CBCA09062A8E0E7400F75B3E /* StaffUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBCA09052A8E0E6B00F75B3E /* StaffUtils.cpp */; }; - CBCA09072A8E0E7D00F75B3E /* StaffUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBCA09052A8E0E6B00F75B3E /* StaffUtils.cpp */; }; CBDEC69B28ED867000C17588 /* GlobalDBSingleton.mm in Sources */ = {isa = PBXBuildFile; fileRef = CBDEC69A28ED867000C17588 /* GlobalDBSingleton.mm */; }; CBFE58292885852B003B94C9 /* ThreadOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CBFE58282885852B003B94C9 /* ThreadOperations.cpp */; }; D7DB6E0F85B2DBE15B01EC21 /* libPods-Comm.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 994BEBDD4E4959F69CEA0BC3 /* libPods-Comm.a */; }; @@ -230,6 +232,10 @@ B7906F6C27209091009BBBF5 /* Thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Thread.h; sourceTree = ""; }; B7E937CA26F448E700022A7C /* Media.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Media.h; sourceTree = ""; }; C562A7004903539402D988CE /* Pods-Comm.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Comm.release.xcconfig"; path = "Target Support Files/Pods-Comm/Pods-Comm.release.xcconfig"; sourceTree = ""; }; + CB1090962AA0C2E000DB02FC /* StaffUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaffUtils.cpp; sourceTree = ""; }; + CB1090972AA0C2E000DB02FC /* StaffUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StaffUtils.h; sourceTree = ""; }; + CB1F33162AA88D5700CDE491 /* CommIOSNotificationsBlobClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommIOSNotificationsBlobClient.h; path = Comm/CommIOSNotifications/CommIOSNotificationsBlobClient.h; sourceTree = ""; }; + CB1F33172AA88D5700CDE491 /* CommIOSNotificationsBlobClient.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CommIOSNotificationsBlobClient.mm; path = Comm/CommIOSNotifications/CommIOSNotificationsBlobClient.mm; sourceTree = ""; }; CB24361629A397AB00FEC4E1 /* NotificationsCryptoModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NotificationsCryptoModule.h; path = Notifications/BackgroundDataStorage/NotificationsCryptoModule.h; sourceTree = ""; }; CB24361729A39A2500FEC4E1 /* NotificationsCryptoModule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NotificationsCryptoModule.cpp; path = Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp; sourceTree = ""; }; CB2688FE2A2DF55F00EC7300 /* CommConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CommConstants.h; sourceTree = ""; }; @@ -263,8 +269,6 @@ CB7EF17C295C580500B17035 /* CommIOSNotificationsBridgeQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CommIOSNotificationsBridgeQueue.h; path = Comm/CommIOSNotifications/CommIOSNotificationsBridgeQueue.h; sourceTree = ""; }; CB7EF17D295C5D1800B17035 /* CommIOSNotifications.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = CommIOSNotifications.mm; path = Comm/CommIOSNotifications/CommIOSNotifications.mm; sourceTree = ""; }; CB90951929531663002F2A7F /* CommIOSNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommIOSNotifications.h; path = Comm/CommIOSNotifications/CommIOSNotifications.h; sourceTree = ""; }; - CBCA09042A8E0E6B00F75B3E /* StaffUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StaffUtils.h; sourceTree = ""; }; - CBCA09052A8E0E6B00F75B3E /* StaffUtils.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = StaffUtils.cpp; sourceTree = ""; }; CBDEC69928ED859600C17588 /* GlobalDBSingleton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GlobalDBSingleton.h; sourceTree = ""; }; CBDEC69A28ED867000C17588 /* GlobalDBSingleton.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = GlobalDBSingleton.mm; path = Comm/GlobalDBSingleton.mm; sourceTree = ""; }; CBFE58272885852B003B94C9 /* ThreadOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadOperations.h; path = PersistentStorageUtilities/ThreadOperationsUtilities/ThreadOperations.h; sourceTree = ""; }; @@ -396,8 +400,8 @@ 71BE84382636A944002849D2 /* Tools */ = { isa = PBXGroup; children = ( - CBCA09052A8E0E6B00F75B3E /* StaffUtils.cpp */, - CBCA09042A8E0E6B00F75B3E /* StaffUtils.h */, + CB1090962AA0C2E000DB02FC /* StaffUtils.cpp */, + CB1090972AA0C2E000DB02FC /* StaffUtils.h */, 7FBB2A7A29EEA2A4002C6493 /* Base64.cpp */, 7FBB2A7929EA752D002C6493 /* Base64.h */, 71B8CCBD26BD4DEB0040C0A2 /* CommSecureStore.h */, @@ -675,6 +679,8 @@ CB90951729531647002F2A7F /* CommIOSNotifications */ = { isa = PBXGroup; children = ( + CB1F33162AA88D5700CDE491 /* CommIOSNotificationsBlobClient.h */, + CB1F33172AA88D5700CDE491 /* CommIOSNotificationsBlobClient.mm */, CB7EF17D295C5D1800B17035 /* CommIOSNotifications.mm */, CB7EF17C295C580500B17035 /* CommIOSNotificationsBridgeQueue.h */, CB7EF17B295C580500B17035 /* CommIOSNotificationsBridgeQueue.mm */, @@ -1042,7 +1048,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - CBCA09062A8E0E7400F75B3E /* StaffUtils.cpp in Sources */, + CB1090992AA0C35200DB02FC /* StaffUtils.cpp in Sources */, 8ED8B5342A4DD4EB00D3DA26 /* CommQueryExecutor.cpp in Sources */, 8EF7756B2A7433630046A385 /* ThreadStore.cpp in Sources */, CB2689002A2DF58000EC7300 /* CommConstants.cpp in Sources */, @@ -1073,14 +1079,15 @@ 7FBB2A7B29EEA2A4002C6493 /* Base64.cpp in Sources */, CB38F2B1286C6C870010535C /* MessageOperationsUtilities.cpp in Sources */, 8EF775712A751B780046A385 /* ReportStore.cpp in Sources */, + CB24361829A39A2500FEC4E1 /* NotificationsCryptoModule.cpp in Sources */, 71CA4A64262DA8E500835C89 /* Logger.mm in Sources */, 71BF5B7F26BBDD7400EDE27D /* CryptoModule.cpp in Sources */, - CB24361829A39A2500FEC4E1 /* NotificationsCryptoModule.cpp in Sources */, 71BE844A2636A944002849D2 /* CommCoreModule.cpp in Sources */, 71D4D7CC26C50B1000FCDBCD /* CommSecureStore.mm in Sources */, 8B38121629CE5742000C52E9 /* RustPromiseManager.cpp in Sources */, 7FBB2A7629E94539002C6493 /* utilsJSI-generated.cpp in Sources */, 711B408425DA97F9005F8F06 /* dummy.swift in Sources */, + CB1F33182AA88D5700CDE491 /* CommIOSNotificationsBlobClient.mm in Sources */, 8E86A6D329537EBB000BBE7D /* DatabaseManager.cpp in Sources */, CBDEC69B28ED867000C17588 /* GlobalDBSingleton.mm in Sources */, 8EA59BD62A6E8E0400EB4F53 /* DraftStore.cpp in Sources */, @@ -1100,13 +1107,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - CBCA09072A8E0E7D00F75B3E /* StaffUtils.cpp in Sources */, + CB1F33192AA88D7900CDE491 /* CommIOSNotificationsBlobClient.mm in Sources */, CB3C0A3B2A125C8F009BD4DA /* NotificationsCryptoModule.cpp in Sources */, CB90951F29534B32002F2A7F /* CommSecureStore.mm in Sources */, CB38B48728771CE500171182 /* TemporaryMessageStorage.mm in Sources */, CB38B48528771CB800171182 /* EncryptedFileUtils.mm in Sources */, CB38B48328771C8300171182 /* NonBlockingLock.mm in Sources */, CB1648AF27CFBE6A00394D9D /* CryptoModule.cpp in Sources */, + CB1090982AA0C2E100DB02FC /* StaffUtils.cpp in Sources */, CB4821AE27CFB187001AB7E1 /* Tools.cpp in Sources */, CB4821AC27CFB17C001AB7E1 /* Session.cpp in Sources */, CB4821A927CFB153001AB7E1 /* WorkerThread.cpp in Sources */, diff --git a/native/ios/Comm/CommIOSNotifications/CommIOSNotificationsBlobClient.h b/native/ios/Comm/CommIOSNotifications/CommIOSNotificationsBlobClient.h new file mode 100644 --- /dev/null +++ b/native/ios/Comm/CommIOSNotifications/CommIOSNotificationsBlobClient.h @@ -0,0 +1,9 @@ +#pragma once + +#import + +@interface CommIOSNotificationsBlobClient : NSObject ++ (id)sharedInstance; +- (void)getAndConsumeSync:(NSString *)blobHash + withSuccessConsumer:(void (^)(NSData *))successConsumer; +@end diff --git a/native/ios/Comm/CommIOSNotifications/CommIOSNotificationsBlobClient.mm b/native/ios/Comm/CommIOSNotifications/CommIOSNotificationsBlobClient.mm new file mode 100644 --- /dev/null +++ b/native/ios/Comm/CommIOSNotifications/CommIOSNotificationsBlobClient.mm @@ -0,0 +1,127 @@ +#import "CommIOSNotificationsBlobClient.h" +#import "Logger.h" + +NSString const *blobServiceAddress = @"https://blob.commtechnologies.org"; +int const blobServiceQueryTimeLimit = 15; + +@interface CommIOSNotificationsBlobClient () +@property(nonatomic, strong) NSURLSession *sharedBlobServiceSession; +@end + +@implementation CommIOSNotificationsBlobClient + ++ (id)sharedInstance { + static CommIOSNotificationsBlobClient *sharedBlobServiceClient = nil; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + NSURLSessionConfiguration *config = + [NSURLSessionConfiguration ephemeralSessionConfiguration]; + // TODO: put necessary authentication into session config + [config setTimeoutIntervalForRequest:blobServiceQueryTimeLimit]; + NSURLSession *session = + [NSURLSession sessionWithConfiguration:config + delegate:nil + delegateQueue:[NSOperationQueue mainQueue]]; + sharedBlobServiceClient = [[self alloc] init]; + sharedBlobServiceClient.sharedBlobServiceSession = session; + }); + return sharedBlobServiceClient; +} + +- (void)getAndConsumeSync:(NSString *)blobHash + withSuccessConsumer:(void (^)(NSData *))successConsumer { + NSError *authTokenError = nil; + NSString *authToken = + [CommIOSNotificationsBlobClient _getAuthToken:&authTokenError]; + + if (authTokenError) { + comm::Logger::log( + "Failed to create blob service auth token. Reason: " + + std::string([authTokenError.localizedDescription UTF8String])); + return; + } + + NSString *blobUrlStr = [blobServiceAddress + stringByAppendingString:[@"/blob/" stringByAppendingString:blobHash]]; + NSURL *blobUrl = [NSURL URLWithString:blobUrlStr]; + NSMutableURLRequest *blobRequest = + [NSMutableURLRequest requestWithURL:blobUrl]; + + // This is slightly against Apple docs: + // https://developer.apple.com/documentation/foundation/nsurlrequest?language=objc#1776617 + // but apparently there is no other way to + // do this and even Apple staff members + // advice to set this field manually to + // achieve token based authentication: + // https://developer.apple.com/forums/thread/89811 + [blobRequest setValue:authToken forHTTPHeaderField:@"Authorization"]; + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + NSURLSessionDataTask *task = [self.sharedBlobServiceSession + dataTaskWithRequest:blobRequest + completionHandler:^( + NSData *_Nullable data, + NSURLResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + comm::Logger::log( + "Failed to download blob from blob service. Reason: " + + std::string([error.localizedDescription UTF8String])); + dispatch_semaphore_signal(semaphore); + return; + } + + successConsumer(data); + dispatch_semaphore_signal(semaphore); + }]; + + [task resume]; + dispatch_semaphore_wait( + semaphore, + dispatch_time( + DISPATCH_TIME_NOW, + (int64_t)(blobServiceQueryTimeLimit * NSEC_PER_SEC))); +} + ++ (NSString *)_getAuthToken:(NSError **)error { + // Authentication data are retrieved on every request + // since they might change while NSE process is running + // so we should not rely on caching them in memory. + + // TODO: retrieve those values from CommSecureStore + NSString *userID = @"placeholder"; + NSString *accessToken = @"placeholder"; + NSString *deviceID = @"placeholder"; + + NSDictionary *jsonAuthObject = @{ + @"userID" : userID, + @"accessToken" : accessToken, + @"deviceID" : deviceID, + }; + + NSData *binaryAuthObject = nil; + NSError *jsonError = nil; + + @try { + binaryAuthObject = [NSJSONSerialization dataWithJSONObject:jsonAuthObject + options:0 + error:&jsonError]; + } @catch (NSException *e) { + *error = [NSError errorWithDomain:@"app.comm" + code:NSFormattingError + userInfo:@{NSLocalizedDescriptionKey : e.reason}]; + return nil; + } + + if (jsonError) { + *error = jsonError; + return nil; + } + + return [@"Bearer " + stringByAppendingString:[binaryAuthObject + base64EncodedStringWithOptions:0]]; +} + +@end 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 @@ -1,7 +1,9 @@ #import "NotificationService.h" +#import "CommIOSNotificationsBlobClient.h" #import "Logger.h" #import "NotificationsCryptoModule.h" #import "TemporaryMessageStorage.h" +#import NSString *const backgroundNotificationTypeKey = @"backgroundNotifType"; NSString *const messageInfosKey = @"messageInfos"; @@ -19,7 +21,8 @@ @property(nonatomic, strong) void (^contentHandler) (UNNotificationContent *contentToDeliver); @property(nonatomic, strong) UNMutableNotificationContent *bestAttemptContent; - +@property(class, nonatomic, strong, readonly) + AESCryptoModuleObjCCompat *processLocalAESCryptoModule; @end @implementation NotificationService @@ -28,6 +31,8 @@ withContentHandler: (void (^)(UNNotificationContent *_Nonnull)) contentHandler { + [NotificationService initializeProcessLocalObjects]; + self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; @@ -49,10 +54,37 @@ comm::Logger::log("NSE: Received erroneously unencrypted notitication."); } - [self persistMessagePayload:self.bestAttemptContent.userInfo]; + if (self.bestAttemptContent.userInfo[@"blobHash"]) { + NSString *blobHash = self.bestAttemptContent.userInfo[@"blobHash"]; + NSData *encryptionKey = [[NSData alloc] + initWithBase64EncodedString:self.bestAttemptContent + .userInfo[@"encryptionKey"] + options:0]; + [[CommIOSNotificationsBlobClient sharedInstance] + getAndConsumeSync:blobHash + withSuccessConsumer:^(NSData *data) { + @try { + NSDictionary *largePayload = + [NotificationService aesDecryptAndParse:data + withKey:encryptionKey]; + [NotificationService persistMessagePayload:largePayload]; + [NotificationService sendNewMessageInfosNotification]; + } @catch (NSException *e) { + comm::Logger::log( + "NSE: Received exception: " + std::string([e.name UTF8String]) + + " with reason: " + std::string([e.reason UTF8String]) + + " during large notification payload processing"); + } + }]; + + self.contentHandler(self.bestAttemptContent); + return; + } + + [NotificationService persistMessagePayload:self.bestAttemptContent.userInfo]; // Message payload persistence is a higher priority task, so it has // to happen prior to potential notification center clearing. - if ([self isRescind:self.bestAttemptContent.userInfo]) { + if ([NotificationService isRescind:self.bestAttemptContent.userInfo]) { [self removeNotificationWithIdentifier:self.bestAttemptContent .userInfo[@"notificationId"]]; self.contentHandler([[UNNotificationContent alloc] init]); @@ -67,7 +99,7 @@ return; } - [self sendNewMessageInfosNotification]; + [NotificationService sendNewMessageInfosNotification]; // TODO modify self.bestAttemptContent here self.contentHandler(self.bestAttemptContent); @@ -77,7 +109,7 @@ // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified // content, otherwise the original push payload will be used. - if ([self isRescind:self.bestAttemptContent.userInfo]) { + if ([NotificationService isRescind:self.bestAttemptContent.userInfo]) { // If we get to this place it means we were unable to // remove relevant notification from notification center in // in time given to NSE to process notification. @@ -126,7 +158,7 @@ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); } -- (void)persistMessagePayload:(NSDictionary *)payload { ++ (void)persistMessagePayload:(NSDictionary *)payload { if (payload[messageInfosKey]) { TemporaryMessageStorage *temporaryStorage = [[TemporaryMessageStorage alloc] init]; @@ -134,7 +166,7 @@ return; } - if (![self isRescind:payload]) { + if (![NotificationService isRescind:payload]) { return; } @@ -159,7 +191,7 @@ [temporaryRescindsStorage writeMessage:serializedRescindPayload]; } -- (BOOL)isRescind:(NSDictionary *)payload { ++ (BOOL)isRescind:(NSDictionary *)payload { return payload[backgroundNotificationTypeKey] && [payload[backgroundNotificationTypeKey] isEqualToString:@"CLEAR"]; } @@ -170,11 +202,11 @@ return !payload[@"threadID"]; } -- (void)sendNewMessageInfosNotification { ++ (void)sendNewMessageInfosNotification { CFNotificationCenterPostNotification( CFNotificationCenterGetDarwinNotifyCenter(), newMessageInfosDarwinNotification, - (__bridge const void *)(self), + nil, nil, TRUE); } @@ -260,4 +292,57 @@ self.bestAttemptContent.userInfo = mutableUserInfo; } ++ (NSDictionary *)aesDecryptAndParse:(NSData *)sealedData + withKey:(NSData *)key { + NSError *decryptError = nil; + NSInteger destinationLength = + [[NotificationService processLocalAESCryptoModule] + decryptedLength:sealedData]; + + NSMutableData *destination = [NSMutableData dataWithLength:destinationLength]; + [[NotificationService processLocalAESCryptoModule] + decryptWithKey:key + sealedData:sealedData + destination:destination + withError:&decryptError]; + + if (decryptError) { + comm::Logger::log( + "NSE: Notification aes decryption failure. Details: " + + std::string([decryptError.localizedDescription UTF8String])); + return nil; + } + + NSString *decryptedSerializedPayload = + [[NSString alloc] initWithData:destination encoding:NSUTF8StringEncoding]; + + return [NSJSONSerialization + JSONObjectWithData:[decryptedSerializedPayload + dataUsingEncoding:NSUTF8StringEncoding] + options:0 + error:nil]; +} + +// Process-local initialization code +// NSE may use different threads and instancess +// of this class to process notifications, but it +// usually keeps the same process for extended +// period of time. Objects that can be initialized +// once and reused on each notification should be +// declared in a method below to avoid unnecessary +// resource usage. + +static AESCryptoModuleObjCCompat *_aesCryptoModule = nil; + ++ (void)initializeProcessLocalObjects { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _aesCryptoModule = [[AESCryptoModuleObjCCompat alloc] init]; + }); +} + ++ (AESCryptoModuleObjCCompat *)processLocalAESCryptoModule { + return _aesCryptoModule; +} + @end