From 527ba2232c4195b4674e82ffc66191002b37da03 Mon Sep 17 00:00:00 2001 From: Ivan Sein Date: Mon, 20 Nov 2017 17:24:26 +0100 Subject: [PATCH] Add Notification Service Extension, decrypt push notifications, modify push notification alerts. Signed-off-by: Ivan Sein --- NotificationServiceExtension/Info.plist | 31 +++ .../NotificationService.h | 13 + .../NotificationService.m | 67 +++++ .../NotificationServiceExtension.entitlements | 10 + Podfile | 9 +- VideoCalls.xcodeproj/project.pbxproj | 253 ++++++++++++++++++ VideoCalls/AppDelegate.m | 5 +- VideoCalls/AuthenticationViewController.m | 17 +- VideoCalls/NCSettingsController.h | 1 + VideoCalls/NCSettingsController.m | 89 ++++-- VideoCalls/VideoCalls.entitlements | 4 + 11 files changed, 466 insertions(+), 33 deletions(-) create mode 100644 NotificationServiceExtension/Info.plist create mode 100644 NotificationServiceExtension/NotificationService.h create mode 100644 NotificationServiceExtension/NotificationService.m create mode 100644 NotificationServiceExtension/NotificationServiceExtension.entitlements diff --git a/NotificationServiceExtension/Info.plist b/NotificationServiceExtension/Info.plist new file mode 100644 index 00000000..3d9c8849 --- /dev/null +++ b/NotificationServiceExtension/Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + NotificationServiceExtension + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionPointIdentifier + com.apple.usernotifications.service + NSExtensionPrincipalClass + NotificationService + + + diff --git a/NotificationServiceExtension/NotificationService.h b/NotificationServiceExtension/NotificationService.h new file mode 100644 index 00000000..890dcaec --- /dev/null +++ b/NotificationServiceExtension/NotificationService.h @@ -0,0 +1,13 @@ +// +// NotificationService.h +// NotificationServiceExtension +// +// Created by Ivan Sein on 14.11.17. +// Copyright © 2017 struktur AG. All rights reserved. +// + +#import + +@interface NotificationService : UNNotificationServiceExtension + +@end diff --git a/NotificationServiceExtension/NotificationService.m b/NotificationServiceExtension/NotificationService.m new file mode 100644 index 00000000..152f8cb3 --- /dev/null +++ b/NotificationServiceExtension/NotificationService.m @@ -0,0 +1,67 @@ +// +// NotificationService.m +// NotificationServiceExtension +// +// Created by Ivan Sein on 14.11.17. +// Copyright © 2017 struktur AG. All rights reserved. +// + +#import "NotificationService.h" + +#import "UICKeyChainStore.h" +#import "NCSettingsController.h" + +@interface NotificationService () + +@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver); +@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent; + +@end + +@implementation NotificationService + +- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { + self.contentHandler = contentHandler; + self.bestAttemptContent = [request.content mutableCopy]; + + self.bestAttemptContent.title = @"Nextcloud notification 🔔"; + self.bestAttemptContent.body = @""; + + NSString *message = [self.bestAttemptContent.userInfo objectForKey:@"subject"]; + NSString *decryptedMessage = nil; + + @try { + UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:@"com.nextcloud.Talk" + accessGroup:@"group.com.nextcloud.Talk"]; + decryptedMessage = [[NCSettingsController sharedInstance] decryptPushNotification:message + withDevicePrivateKey:[keychain dataForKey:kNCPNPrivateKey]]; + } @catch (NSException *exception) { + NSLog(@"An error ocurred decrypting the message. %@", exception); + } + + if (decryptedMessage) { + NSData *data = [decryptedMessage dataUsingEncoding:NSUTF8StringEncoding]; + id messageJSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; + + decryptedMessage = [messageJSON objectForKey:@"subject"]; + self.bestAttemptContent.body = decryptedMessage; + + NSString *appId = [messageJSON objectForKey:@"app"]; + if ([appId isEqualToString:@"spreed"]) { + self.bestAttemptContent.title = @"Talk notification 📞"; + } + } + + self.contentHandler(self.bestAttemptContent); +} + +- (void)serviceExtensionTimeWillExpire { + // 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. + self.bestAttemptContent.title = @"Nextcloud notification 🔔"; + self.bestAttemptContent.body = @""; + + self.contentHandler(self.bestAttemptContent); +} + +@end diff --git a/NotificationServiceExtension/NotificationServiceExtension.entitlements b/NotificationServiceExtension/NotificationServiceExtension.entitlements new file mode 100644 index 00000000..c8cc86a4 --- /dev/null +++ b/NotificationServiceExtension/NotificationServiceExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.nextcloud.Talk + + + diff --git a/Podfile b/Podfile index 8a792306..3afbf146 100644 --- a/Podfile +++ b/Podfile @@ -1,10 +1,15 @@ source 'https://github.com/CocoaPods/Specs.git' -target "VideoCalls" do - platform :ios, '9.0' + +target "VideoCalls" do pod 'AFNetworking', '~> 2.5' pod 'DateTools' pod 'GoogleWebRTC' pod 'Firebase/Core' pod 'Firebase/Messaging' end + +target "NotificationServiceExtension" do +pod 'AFNetworking', '~> 2.5' +end + diff --git a/VideoCalls.xcodeproj/project.pbxproj b/VideoCalls.xcodeproj/project.pbxproj index 9f2046fe..2f379353 100644 --- a/VideoCalls.xcodeproj/project.pbxproj +++ b/VideoCalls.xcodeproj/project.pbxproj @@ -14,6 +14,15 @@ 2C0574A41EDDA2E300D9E7F2 /* LoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C0574A21EDDA2E300D9E7F2 /* LoginViewController.m */; }; 2C0574A51EDDA2E300D9E7F2 /* LoginViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2C0574A31EDDA2E300D9E7F2 /* LoginViewController.xib */; }; 2C2E64251F3462AF00D39CE8 /* NCSignalingMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C2E64241F3462AF00D39CE8 /* NCSignalingMessage.m */; }; + 2C3F6AA81FBB1ACD00E2705C /* NotificationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C3F6AA71FBB1ACD00E2705C /* NotificationService.m */; }; + 2C3F6AAC1FBB1ACD00E2705C /* NotificationServiceExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 2C3F6AA41FBB1ACC00E2705C /* NotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 2C3F6AB31FBB45E600E2705C /* NCSettingsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CA1CC941F014EF9002FE6A2 /* NCSettingsController.m */; }; + 2C3F6AB41FBB45FD00E2705C /* libcrypto.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C8B60131FB4A9DA006E87EF /* libcrypto.a */; }; + 2C3F6AB51FBB460100E2705C /* libssl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C8B60141FB4A9DA006E87EF /* libssl.a */; }; + 2C3F6AB61FBB47B800E2705C /* NCAPIController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CA1CCA91F02D1A4002FE6A2 /* NCAPIController.m */; }; + 2C3F6AB71FBB47C400E2705C /* UICKeyChainStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CA1CC991F0161EA002FE6A2 /* UICKeyChainStore.m */; }; + 2C3F6AB81FBB4C9500E2705C /* NCRoom.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CA1CCC21F166CC5002FE6A2 /* NCRoom.m */; }; + 2C3F6AB91FBB4C9D00E2705C /* NCUser.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CA1CCCC1F181741002FE6A2 /* NCUser.m */; }; 2C4D7D631F2F7C2C00FF4A0D /* ARDCaptureController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C4D7D621F2F7C2C00FF4A0D /* ARDCaptureController.m */; }; 2C4D7D691F2F7DBC00FF4A0D /* ARDSettingsModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C4D7D651F2F7DBC00FF4A0D /* ARDSettingsModel.m */; }; 2C4D7D6A1F2F7DBC00FF4A0D /* ARDSettingsStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C4D7D681F2F7DBC00FF4A0D /* ARDSettingsStore.m */; }; @@ -54,9 +63,34 @@ 2CA1CCDB1F1F6FCA002FE6A2 /* RoomTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CA1CCD91F1F6FCA002FE6A2 /* RoomTableViewCell.m */; }; 2CB5D0531FB4D1FD00D7A5B7 /* libssl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C8B60141FB4A9DA006E87EF /* libssl.a */; }; 2CB5D0541FB4D20B00D7A5B7 /* libcrypto.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C8B60131FB4A9DA006E87EF /* libcrypto.a */; }; + 85032FFE8557E8F29A3C79B8 /* libPods-NotificationServiceExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 49EC325ACC97A400B9FAFA27 /* libPods-NotificationServiceExtension.a */; }; DB6A892B5CEBD4812F7C52EF /* libPods-VideoCalls.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6267808DF11BEB859C0BE9F1 /* libPods-VideoCalls.a */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 2C3F6AAA1FBB1ACD00E2705C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 2C0574751EDD9E8E00D9E7F2 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2C3F6AA31FBB1ACC00E2705C; + remoteInfo = NotificationServiceExtension; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 2C3F6AB01FBB1ACD00E2705C /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 2C3F6AAC1FBB1ACD00E2705C /* NotificationServiceExtension.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 2C05747D1EDD9E8E00D9E7F2 /* VideoCalls.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VideoCalls.app; sourceTree = BUILT_PRODUCTS_DIR; }; 2C0574811EDD9E8E00D9E7F2 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; @@ -70,6 +104,10 @@ 2C0574A31EDDA2E300D9E7F2 /* LoginViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LoginViewController.xib; sourceTree = ""; }; 2C2E64231F3462AF00D39CE8 /* NCSignalingMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NCSignalingMessage.h; sourceTree = ""; }; 2C2E64241F3462AF00D39CE8 /* NCSignalingMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NCSignalingMessage.m; sourceTree = ""; }; + 2C3F6AA41FBB1ACC00E2705C /* NotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 2C3F6AA61FBB1ACD00E2705C /* NotificationService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NotificationService.h; sourceTree = ""; }; + 2C3F6AA71FBB1ACD00E2705C /* NotificationService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationService.m; sourceTree = ""; }; + 2C3F6AA91FBB1ACD00E2705C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 2C4D7D611F2F7C2C00FF4A0D /* ARDCaptureController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARDCaptureController.h; sourceTree = ""; }; 2C4D7D621F2F7C2C00FF4A0D /* ARDCaptureController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARDCaptureController.m; sourceTree = ""; }; 2C4D7D641F2F7DBC00FF4A0D /* ARDSettingsModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARDSettingsModel.h; sourceTree = ""; }; @@ -212,7 +250,11 @@ 2CA1CCD51F1E664C002FE6A2 /* ContactsTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ContactsTableViewCell.xib; sourceTree = ""; }; 2CA1CCD81F1F6FCA002FE6A2 /* RoomTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RoomTableViewCell.h; sourceTree = ""; }; 2CA1CCD91F1F6FCA002FE6A2 /* RoomTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RoomTableViewCell.m; sourceTree = ""; }; + 2CF6F5E21FC211EC00E4EFAA /* NotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationServiceExtension.entitlements; sourceTree = ""; }; + 49EC325ACC97A400B9FAFA27 /* libPods-NotificationServiceExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NotificationServiceExtension.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 5EF849CBD848BB3194C0D885 /* Pods-NotificationServiceExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationServiceExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.debug.xcconfig"; sourceTree = ""; }; 6267808DF11BEB859C0BE9F1 /* libPods-VideoCalls.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VideoCalls.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 73D489B0CDBE660CF768BABE /* Pods-NotificationServiceExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationServiceExtension.release.xcconfig"; path = "Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.release.xcconfig"; sourceTree = ""; }; 8BD08DF13BF3479DEEECFEB1 /* Pods-VideoCalls.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoCalls.debug.xcconfig"; path = "Pods/Target Support Files/Pods-VideoCalls/Pods-VideoCalls.debug.xcconfig"; sourceTree = ""; }; E8DF4DF89BBD55A5ED6488CB /* Pods-VideoCalls.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoCalls.release.xcconfig"; path = "Pods/Target Support Files/Pods-VideoCalls/Pods-VideoCalls.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -234,12 +276,23 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 2C3F6AA11FBB1ACC00E2705C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2C3F6AB51FBB460100E2705C /* libssl.a in Frameworks */, + 2C3F6AB41FBB45FD00E2705C /* libcrypto.a in Frameworks */, + 85032FFE8557E8F29A3C79B8 /* libPods-NotificationServiceExtension.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 2C0574741EDD9E8E00D9E7F2 = { isa = PBXGroup; children = ( + 2C3F6AA51FBB1ACC00E2705C /* NotificationServiceExtension */, 2C05747E1EDD9E8E00D9E7F2 /* Products */, 2C05749C1EDDA01700D9E7F2 /* ThirdParty */, 2C05747F1EDD9E8E00D9E7F2 /* VideoCalls */, @@ -252,6 +305,7 @@ isa = PBXGroup; children = ( 2C05747D1EDD9E8E00D9E7F2 /* VideoCalls.app */, + 2C3F6AA41FBB1ACC00E2705C /* NotificationServiceExtension.appex */, ); name = Products; sourceTree = ""; @@ -298,6 +352,17 @@ name = ThirdParty; sourceTree = ""; }; + 2C3F6AA51FBB1ACC00E2705C /* NotificationServiceExtension */ = { + isa = PBXGroup; + children = ( + 2CF6F5E21FC211EC00E4EFAA /* NotificationServiceExtension.entitlements */, + 2C3F6AA61FBB1ACD00E2705C /* NotificationService.h */, + 2C3F6AA71FBB1ACD00E2705C /* NotificationService.m */, + 2C3F6AA91FBB1ACD00E2705C /* Info.plist */, + ); + path = NotificationServiceExtension; + sourceTree = ""; + }; 2C4D7D601F2F7C2C00FF4A0D /* AppRTC */ = { isa = PBXGroup; children = ( @@ -507,6 +572,7 @@ 2C90E5661EDDE1340093D85A /* CoreGraphics.framework */, 2C90E5631EDDE0FB0093D85A /* Foundation.framework */, 6267808DF11BEB859C0BE9F1 /* libPods-VideoCalls.a */, + 49EC325ACC97A400B9FAFA27 /* libPods-NotificationServiceExtension.a */, ); name = Frameworks; sourceTree = ""; @@ -535,6 +601,8 @@ children = ( 8BD08DF13BF3479DEEECFEB1 /* Pods-VideoCalls.debug.xcconfig */, E8DF4DF89BBD55A5ED6488CB /* Pods-VideoCalls.release.xcconfig */, + 5EF849CBD848BB3194C0D885 /* Pods-NotificationServiceExtension.debug.xcconfig */, + 73D489B0CDBE660CF768BABE /* Pods-NotificationServiceExtension.release.xcconfig */, ); name = Pods; sourceTree = ""; @@ -553,16 +621,37 @@ 29696B57B26EBF9C28436CE8 /* [CP] Embed Pods Frameworks */, A3C686B1B84C4462F93441AB /* [CP] Copy Pods Resources */, 2C8035721F950BA800501B5C /* ShellScript */, + 2C3F6AB01FBB1ACD00E2705C /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( + 2C3F6AAB1FBB1ACD00E2705C /* PBXTargetDependency */, ); name = VideoCalls; productName = VideoCalls; productReference = 2C05747D1EDD9E8E00D9E7F2 /* VideoCalls.app */; productType = "com.apple.product-type.application"; }; + 2C3F6AA31FBB1ACC00E2705C /* NotificationServiceExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2C3F6AAF1FBB1ACD00E2705C /* Build configuration list for PBXNativeTarget "NotificationServiceExtension" */; + buildPhases = ( + BD6E17ECE1FB7C457F620E30 /* [CP] Check Pods Manifest.lock */, + 2C3F6AA01FBB1ACC00E2705C /* Sources */, + 2C3F6AA11FBB1ACC00E2705C /* Frameworks */, + 2C3F6AA21FBB1ACC00E2705C /* Resources */, + 93097A6DB9AF5BB051C5E3E9 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = NotificationServiceExtension; + productName = NotificationServiceExtension; + productReference = 2C3F6AA41FBB1ACC00E2705C /* NotificationServiceExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -577,14 +666,33 @@ DevelopmentTeam = NKUJUXUJ3B; ProvisioningStyle = Automatic; SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; com.apple.BackgroundModes = { enabled = 1; }; + com.apple.Keychain = { + enabled = 0; + }; com.apple.Push = { enabled = 1; }; }; }; + 2C3F6AA31FBB1ACC00E2705C = { + CreatedOnToolsVersion = 9.0.1; + DevelopmentTeam = NKUJUXUJ3B; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; + com.apple.Keychain = { + enabled = 0; + }; + }; + }; }; }; buildConfigurationList = 2C0574781EDD9E8E00D9E7F2 /* Build configuration list for PBXProject "VideoCalls" */; @@ -601,6 +709,7 @@ projectRoot = ""; targets = ( 2C05747C1EDD9E8E00D9E7F2 /* VideoCalls */, + 2C3F6AA31FBB1ACC00E2705C /* NotificationServiceExtension */, ); }; /* End PBXProject section */ @@ -621,6 +730,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 2C3F6AA21FBB1ACC00E2705C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -673,6 +789,21 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 93097A6DB9AF5BB051C5E3E9 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; A3C686B1B84C4462F93441AB /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -691,6 +822,24 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-VideoCalls/Pods-VideoCalls-resources.sh\"\n"; showEnvVarsInLog = 0; }; + BD6E17ECE1FB7C457F620E30 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-NotificationServiceExtension-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -732,8 +881,29 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 2C3F6AA01FBB1ACC00E2705C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2C3F6AB61FBB47B800E2705C /* NCAPIController.m in Sources */, + 2C3F6AB91FBB4C9D00E2705C /* NCUser.m in Sources */, + 2C3F6AA81FBB1ACD00E2705C /* NotificationService.m in Sources */, + 2C3F6AB31FBB45E600E2705C /* NCSettingsController.m in Sources */, + 2C3F6AB71FBB47C400E2705C /* UICKeyChainStore.m in Sources */, + 2C3F6AB81FBB4C9500E2705C /* NCRoom.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 2C3F6AAB1FBB1ACD00E2705C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2C3F6AA31FBB1ACC00E2705C /* NotificationServiceExtension */; + targetProxy = 2C3F6AAA1FBB1ACD00E2705C /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ 2C05748C1EDD9E8E00D9E7F2 /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -932,6 +1102,80 @@ }; name = Release; }; + 2C3F6AAD1FBB1ACD00E2705C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5EF849CBD848BB3194C0D885 /* Pods-NotificationServiceExtension.debug.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = NKUJUXUJ3B; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_C_LANGUAGE_STANDARD = c11; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"$(PROJECT_DIR)/ThirdParty\"/**", + ); + INFOPLIST_FILE = NotificationServiceExtension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/ThirdParty/openssl", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.nextcloud.Talk.NotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 2C3F6AAE1FBB1ACD00E2705C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 73D489B0CDBE660CF768BABE /* Pods-NotificationServiceExtension.release.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; + CODE_SIGN_IDENTITY = "iPhone Distribution: Nextcloud GmbH (NKUJUXUJ3B)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = NKUJUXUJ3B; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_C_LANGUAGE_STANDARD = c11; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"$(PROJECT_DIR)/ThirdParty\"/**", + ); + INFOPLIST_FILE = NotificationServiceExtension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/ThirdParty/openssl", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.nextcloud.Talk.NotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -953,6 +1197,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 2C3F6AAF1FBB1ACD00E2705C /* Build configuration list for PBXNativeTarget "NotificationServiceExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2C3F6AAD1FBB1ACD00E2705C /* Debug */, + 2C3F6AAE1FBB1ACD00E2705C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 2C0574751EDD9E8E00D9E7F2 /* Project object */; diff --git a/VideoCalls/AppDelegate.m b/VideoCalls/AppDelegate.m index 5c68c868..58470877 100644 --- a/VideoCalls/AppDelegate.m +++ b/VideoCalls/AppDelegate.m @@ -63,7 +63,6 @@ return YES; } - - (void)applicationWillResignActive:(UIApplication *)application { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. @@ -101,7 +100,9 @@ { NSLog(@"FCM registration token: %@", fcmToken); [NCSettingsController sharedInstance].ncPushToken = fcmToken; - [UICKeyChainStore setString:fcmToken forKey:kNCPushTokenKey]; + UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:@"com.nextcloud.Talk" + accessGroup:@"group.com.nextcloud.Talk"]; + [keychain setString:fcmToken forKey:kNCPushTokenKey]; } diff --git a/VideoCalls/AuthenticationViewController.m b/VideoCalls/AuthenticationViewController.m index e1dc88d8..574bc095 100644 --- a/VideoCalls/AuthenticationViewController.m +++ b/VideoCalls/AuthenticationViewController.m @@ -98,9 +98,12 @@ NSString * const NCLoginCompletedNotification = @"NCLoginCompletedNotification [NCSettingsController sharedInstance].ncUser = user; [NCSettingsController sharedInstance].ncToken = token; - [UICKeyChainStore setString:_serverUrl forKey:kNCServerKey]; - [UICKeyChainStore setString:user forKey:kNCUserKey]; - [UICKeyChainStore setString:token forKey:kNCTokenKey]; + UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:@"com.nextcloud.Talk" + accessGroup:@"group.com.nextcloud.Talk"]; + + [keychain setString:_serverUrl forKey:kNCServerKey]; + [keychain setString:user forKey:kNCUserKey]; + [keychain setString:token forKey:kNCTokenKey]; [[NCAPIController sharedInstance] setNCServer:_serverUrl]; [[NCAPIController sharedInstance] setAuthHeaderWithUser:user andToken:token]; @@ -112,7 +115,7 @@ NSString * const NCLoginCompletedNotification = @"NCLoginCompletedNotification if (!error) { NSString *userDisplayName = [userProfile objectForKey:@"displayname"]; [NCSettingsController sharedInstance].ncUserDisplayName = userDisplayName; - [UICKeyChainStore setString:userDisplayName forKey:kNCUserDisplayNameKey]; + [keychain setString:userDisplayName forKey:kNCUserDisplayNameKey]; } else { NSLog(@"Error while getting the user profile"); } @@ -131,9 +134,9 @@ NSString * const NCLoginCompletedNotification = @"NCLoginCompletedNotification [NCSettingsController sharedInstance].ncDeviceIdentifier = deviceIdentifier; [NCSettingsController sharedInstance].ncDeviceSignature = signature; - [UICKeyChainStore setString:publicKey forKey:kNCUserPublicKey]; - [UICKeyChainStore setString:deviceIdentifier forKey:kNCDeviceIdentifier]; - [UICKeyChainStore setString:signature forKey:kNCDeviceSignature]; + [keychain setString:publicKey forKey:kNCUserPublicKey]; + [keychain setString:deviceIdentifier forKey:kNCDeviceIdentifier]; + [keychain setString:signature forKey:kNCDeviceSignature]; [[NCAPIController sharedInstance] subscribeToPushServer:^(NSError *error, NSInteger errorCode) { if (!error) { diff --git a/VideoCalls/NCSettingsController.h b/VideoCalls/NCSettingsController.h index af6c4d9e..0a79f236 100644 --- a/VideoCalls/NCSettingsController.h +++ b/VideoCalls/NCSettingsController.h @@ -41,5 +41,6 @@ extern NSString * const kNCUserPublicKey; - (void)cleanUserAndServerStoredValues; - (BOOL)generatePushNotificationsKeyPair; - (NSString *)pushTokenSHA512; +- (NSString *)decryptPushNotification:(NSString *)message withDevicePrivateKey:(NSData *)privateKey; @end diff --git a/VideoCalls/NCSettingsController.m b/VideoCalls/NCSettingsController.m index 7d876607..c6e3fb48 100644 --- a/VideoCalls/NCSettingsController.m +++ b/VideoCalls/NCSettingsController.m @@ -13,10 +13,17 @@ #import #import #import +#import #import - #import "NCAPIController.h" +@interface NCSettingsController () +{ + UICKeyChainStore *_keychain; +} + +@end + @implementation NCSettingsController NSString * const kNCServerKey = @"ncServer"; @@ -45,6 +52,8 @@ NSString * const kNCUserPublicKey = @"ncUserPublicKey"; { self = [super init]; if (self) { + _keychain = [UICKeyChainStore keyChainStoreWithService:@"com.nextcloud.Talk" + accessGroup:@"group.com.nextcloud.Talk"]; [self readValuesFromKeyChain]; } return self; @@ -54,16 +63,16 @@ NSString * const kNCUserPublicKey = @"ncUserPublicKey"; - (void)readValuesFromKeyChain { - _ncServer = [UICKeyChainStore stringForKey:kNCServerKey]; - _ncUser = [UICKeyChainStore stringForKey:kNCUserKey]; - _ncUserDisplayName = [UICKeyChainStore stringForKey:kNCUserDisplayNameKey]; - _ncToken = [UICKeyChainStore stringForKey:kNCTokenKey]; - _ncPushToken = [UICKeyChainStore stringForKey:kNCPushTokenKey]; - _ncPNPublicKey = [UICKeyChainStore dataForKey:kNCPNPublicKey]; - _ncPNPrivateKey = [UICKeyChainStore dataForKey:kNCPNPrivateKey]; - _ncDeviceIdentifier = [UICKeyChainStore stringForKey:kNCDeviceIdentifier]; - _ncDeviceSignature = [UICKeyChainStore stringForKey:kNCDeviceSignature]; - _ncUserPublicKey = [UICKeyChainStore stringForKey:kNCUserPublicKey]; + _ncServer = [_keychain stringForKey:kNCServerKey]; + _ncUser = [_keychain stringForKey:kNCUserKey]; + _ncUserDisplayName = [_keychain stringForKey:kNCUserDisplayNameKey]; + _ncToken = [_keychain stringForKey:kNCTokenKey]; + _ncPushToken = [_keychain stringForKey:kNCPushTokenKey]; + _ncPNPublicKey = [_keychain dataForKey:kNCPNPublicKey]; + _ncPNPrivateKey = [_keychain dataForKey:kNCPNPrivateKey]; + _ncDeviceIdentifier = [_keychain stringForKey:kNCDeviceIdentifier]; + _ncDeviceSignature = [_keychain stringForKey:kNCDeviceSignature]; + _ncUserPublicKey = [_keychain stringForKey:kNCUserPublicKey]; } - (void)cleanUserAndServerStoredValues @@ -78,15 +87,15 @@ NSString * const kNCUserPublicKey = @"ncUserPublicKey"; _ncDeviceIdentifier = nil; _ncDeviceSignature = nil; - [UICKeyChainStore removeItemForKey:kNCServerKey]; - [UICKeyChainStore removeItemForKey:kNCUserKey]; - [UICKeyChainStore removeItemForKey:kNCUserDisplayNameKey]; - [UICKeyChainStore removeItemForKey:kNCTokenKey]; - [UICKeyChainStore removeItemForKey:kNCPNPublicKey]; - [UICKeyChainStore removeItemForKey:kNCPNPrivateKey]; - [UICKeyChainStore removeItemForKey:kNCDeviceIdentifier]; - [UICKeyChainStore removeItemForKey:kNCDeviceSignature]; - [UICKeyChainStore removeItemForKey:kNCUserPublicKey]; + [_keychain removeItemForKey:kNCServerKey]; + [_keychain removeItemForKey:kNCUserKey]; + [_keychain removeItemForKey:kNCUserDisplayNameKey]; + [_keychain removeItemForKey:kNCTokenKey]; + [_keychain removeItemForKey:kNCPNPublicKey]; + [_keychain removeItemForKey:kNCPNPrivateKey]; + [_keychain removeItemForKey:kNCDeviceIdentifier]; + [_keychain removeItemForKey:kNCDeviceSignature]; + [_keychain removeItemForKey:kNCUserPublicKey]; #warning TODO - Restore NCAPIController in a diferent way [[NCAPIController sharedInstance] setAuthHeaderWithUser:NULL andToken:NULL]; @@ -116,7 +125,7 @@ NSString * const kNCUserPublicKey = @"ncUserPublicKey"; BIO_read(publicKeyBIO, keyBytes, len); _ncPNPublicKey = [NSData dataWithBytes:keyBytes length:len]; - [UICKeyChainStore setData:_ncPNPublicKey forKey:kNCPNPublicKey]; + [_keychain setData:_ncPNPublicKey forKey:kNCPNPublicKey]; NSLog(@"Push Notifications Key Pair generated: \n%@", [[NSString alloc] initWithData:_ncPNPublicKey encoding:NSUTF8StringEncoding]); // PrivateKey @@ -128,7 +137,7 @@ NSString * const kNCUserPublicKey = @"ncUserPublicKey"; BIO_read(privateKeyBIO, keyBytes, len); _ncPNPrivateKey = [NSData dataWithBytes:keyBytes length:len]; - [UICKeyChainStore setData:_ncPNPrivateKey forKey:kNCPNPrivateKey]; + [_keychain setData:_ncPNPrivateKey forKey:kNCPNPrivateKey]; EVP_PKEY_free(pkey); @@ -165,6 +174,42 @@ cleanup: return pkey; } +- (NSString *)decryptPushNotification:(NSString *)message withDevicePrivateKey:(NSData *)privateKey +{ + NSString *privateKeyString = [[NSString alloc] initWithData:privateKey encoding:NSUTF8StringEncoding]; + NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:message options:0]; + char *privKey = (char *)[privateKeyString UTF8String]; + + // Get Device Private Key from PEM + BIO *bio = BIO_new(BIO_s_mem()); + BIO_write(bio, privKey, (int)strlen(privKey)); + + EVP_PKEY* pkey = 0; + PEM_read_bio_PrivateKey(bio, &pkey, 0, 0); + + RSA* rsa = EVP_PKEY_get1_RSA(pkey); + + // Decrypt the message + unsigned char *decrypted = (unsigned char *) malloc(4096); + + int decrypted_length = RSA_private_decrypt((int)[decodedData length], [decodedData bytes], decrypted, rsa, RSA_PKCS1_PADDING); + if(decrypted_length == -1) { + char buffer[500]; + ERR_error_string(ERR_get_error(), buffer); + NSLog(@"%@",[NSString stringWithUTF8String:buffer]); + return nil; + } + + NSString *decryptString = [[NSString alloc] initWithBytes:decrypted length:decrypted_length encoding:NSUTF8StringEncoding]; + + if (decrypted) + free(decrypted); + free(bio); + free(rsa); + + return decryptString; +} + - (NSString *)pushTokenSHA512 { return [self createSHA512:_ncPushToken]; diff --git a/VideoCalls/VideoCalls.entitlements b/VideoCalls/VideoCalls.entitlements index 903def2a..2c059bbb 100644 --- a/VideoCalls/VideoCalls.entitlements +++ b/VideoCalls/VideoCalls.entitlements @@ -4,5 +4,9 @@ aps-environment development + com.apple.security.application-groups + + group.com.nextcloud.Talk +