From 2d2f5aa6a6b3309483bc0864f99db4d29944d714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sun, 21 Dec 2025 21:36:33 +0100 Subject: [PATCH 1/2] chore: Replace openSSL with native methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- NextcloudTalk.xcodeproj/project.pbxproj | 139 ++++++++++++++---- NextcloudTalk/AppDelegate.m | 5 +- .../NCPushNotificationKeyPair.swift | 16 ++ .../Notifications/NCPushNotificationsUtils.h | 16 -- .../Notifications/NCPushNotificationsUtils.m | 53 ------- .../NCPushNotificationsUtils.swift | 43 ++++++ NextcloudTalk/Security/CCCertificate.m | 59 +++----- NextcloudTalk/Settings/NCSettingsController.h | 1 - NextcloudTalk/Settings/NCSettingsController.m | 100 +------------ .../NotificationService.m | 3 +- 10 files changed, 196 insertions(+), 239 deletions(-) create mode 100644 NextcloudTalk/Notifications/NCPushNotificationKeyPair.swift delete mode 100644 NextcloudTalk/Notifications/NCPushNotificationsUtils.h delete mode 100644 NextcloudTalk/Notifications/NCPushNotificationsUtils.m create mode 100644 NextcloudTalk/Notifications/NCPushNotificationsUtils.swift diff --git a/NextcloudTalk.xcodeproj/project.pbxproj b/NextcloudTalk.xcodeproj/project.pbxproj index 4c23410d1..d29c71252 100644 --- a/NextcloudTalk.xcodeproj/project.pbxproj +++ b/NextcloudTalk.xcodeproj/project.pbxproj @@ -40,7 +40,6 @@ 1F759C092B63B9A7000534AB /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 1F759C082B63B9A7000534AB /* SDWebImage */; }; 1F759C0B2B63B9A7000534AB /* SDWebImageSVGKitPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 1F759C0A2B63B9A7000534AB /* SDWebImageSVGKitPlugin */; }; 1F759C0E2B63B9BA000534AB /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = 1F759C0D2B63B9BA000534AB /* WebRTC */; }; - 1F759C102B63B9D9000534AB /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = 1F759C0F2B63B9D9000534AB /* OpenSSL */; }; 1F759C162B63B9D9000534AB /* NextcloudKit in Frameworks */ = {isa = PBXBuildFile; productRef = 1F759C152B63B9D9000534AB /* NextcloudKit */; }; 1F759C182B63B9D9000534AB /* SwiftyAttributes in Frameworks */ = {isa = PBXBuildFile; productRef = 1F759C172B63B9D9000534AB /* SwiftyAttributes */; }; 1F759C1A2B63B9D9000534AB /* CDMarkdownKit in Frameworks */ = {isa = PBXBuildFile; productRef = 1F759C192B63B9D9000534AB /* CDMarkdownKit */; }; @@ -61,6 +60,16 @@ 1F7AE07C29142E6A009F72AD /* NextcloudKit in Frameworks */ = {isa = PBXBuildFile; productRef = 1F7AE07B29142E6A009F72AD /* NextcloudKit */; }; 1F7AE07D29158878009F72AD /* IntentsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F90EFC225FE489B00F3FA55 /* IntentsUI.framework */; }; 1F8848122A75B68D00063860 /* IntentsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F90EFC225FE489B00F3FA55 /* IntentsUI.framework */; }; + 1F899EFF2EF866F500890727 /* SwiftyRSA in Frameworks */ = {isa = PBXBuildFile; productRef = 1F899EFE2EF866F500890727 /* SwiftyRSA */; }; + 1F899F012EF8688D00890727 /* SwiftyRSA in Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F002EF8688D00890727 /* SwiftyRSA */; }; + 1F899F022EF8688D00890727 /* SwiftyRSA in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F002EF8688D00890727 /* SwiftyRSA */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 1F899F082EF8698200890727 /* SwiftyRSA in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 1F899EFE2EF866F500890727 /* SwiftyRSA */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 1F899F142EF889E500890727 /* SwiftyRSA in Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F132EF889E500890727 /* SwiftyRSA */; }; + 1F899F152EF889E500890727 /* SwiftyRSA in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F132EF889E500890727 /* SwiftyRSA */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 1F899F172EF889EE00890727 /* SwiftyRSA in Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F162EF889EE00890727 /* SwiftyRSA */; }; + 1F899F182EF889EE00890727 /* SwiftyRSA in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F162EF889EE00890727 /* SwiftyRSA */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 1F899F1B2EF889F300890727 /* SwiftyRSA in Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F1A2EF889F300890727 /* SwiftyRSA */; }; + 1F899F1C2EF889F300890727 /* SwiftyRSA in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F1A2EF889F300890727 /* SwiftyRSA */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 1F90EFC725FE4BE700F3FA55 /* IntentsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F90EFC225FE489B00F3FA55 /* IntentsUI.framework */; }; 1F96D76E2D93013C0088331F /* SwiftyXMLParser in Frameworks */ = {isa = PBXBuildFile; productRef = 1F96D76D2D93013C0088331F /* SwiftyXMLParser */; }; 1FA93DA52D70FCC200DF6CDF /* TalkIntents.appex in Embed ExtensionKit Extensions */ = {isa = PBXBuildFile; fileRef = 1FA93D9C2D70FCC200DF6CDF /* TalkIntents.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -144,7 +153,6 @@ 2CC1C38629C0945700C8436B /* DRCellSlideGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CC1C38029C0945600C8436B /* DRCellSlideGestureRecognizer.m */; }; 2CC1C38729C0945700C8436B /* DRCellSlideAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CC1C38429C0945700C8436B /* DRCellSlideAction.m */; }; 2CC1C38829C0945700C8436B /* DRCellSlideActionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CC1C38529C0945700C8436B /* DRCellSlideActionView.m */; }; - 2CCCD21D2835088F00F076CE /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = 2CCCD21C2835088F00F076CE /* OpenSSL */; }; 3FCA62550CD1442D28E8A7C6 /* libPods-NotificationServiceExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B81BB7A4920C391CC2CACFD /* libPods-NotificationServiceExtension.a */; }; 4890175925A0D7FC2EC76CC0 /* libPods-NextcloudTalkTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7005E22D6C2896927FC3AEEC /* libPods-NextcloudTalkTests.a */; }; 80CDF8C42A8E098900CB57AE /* SwiftUIIntrospect in Frameworks */ = {isa = PBXBuildFile; productRef = 80CDF8C32A8E098900CB57AE /* SwiftUIIntrospect */; }; @@ -199,6 +207,61 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 1F899F032EF8688D00890727 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 1F899F022EF8688D00890727 /* SwiftyRSA in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + 1F899F072EF8689500890727 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 1F899F152EF889E500890727 /* SwiftyRSA in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + 1F899F092EF8698200890727 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 1F899F082EF8698200890727 /* SwiftyRSA in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + 1F899F192EF889EE00890727 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 1F899F182EF889EE00890727 /* SwiftyRSA in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + 1F899F1D2EF889F300890727 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 1F899F1C2EF889F300890727 /* SwiftyRSA in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; 1FA93DAA2D70FCC200DF6CDF /* Embed ExtensionKit Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -1122,7 +1185,6 @@ 1F759C092B63B9A7000534AB /* SDWebImage in Frameworks */, 1F759C1A2B63B9D9000534AB /* CDMarkdownKit in Frameworks */, 1F759C0B2B63B9A7000534AB /* SDWebImageSVGKitPlugin in Frameworks */, - 1F759C102B63B9D9000534AB /* OpenSSL in Frameworks */, 1F759C342B63CBAA000534AB /* Realm in Frameworks */, 1F759C162B63B9D9000534AB /* NextcloudKit in Frameworks */, 1F759C182B63B9D9000534AB /* SwiftyAttributes in Frameworks */, @@ -1139,6 +1201,7 @@ 1FA93DE92D71011800DF6CDF /* Realm in Frameworks */, 1FA93DE72D71011400DF6CDF /* NextcloudKit in Frameworks */, 1FA93DF12D71012900DF6CDF /* UICKeyChainStore in Frameworks */, + 1F899F1B2EF889F300890727 /* SwiftyRSA in Frameworks */, 1FA93DEB2D71011C00DF6CDF /* SDWebImage in Frameworks */, 1FA93DE52D71010F00DF6CDF /* CDMarkdownKit in Frameworks */, 1FA93DED2D71012000DF6CDF /* SDWebImageSVGKitPlugin in Frameworks */, @@ -1165,6 +1228,7 @@ 1FC4B32B2CC0564100D28138 /* UICKeyChainStore in Frameworks */, 1FF2FD5D2AB99CCB000C9905 /* ReplayKit.framework in Frameworks */, 1F77A5EF2AB9A41E007B6037 /* SDWebImage in Frameworks */, + 1F899F172EF889EE00890727 /* SwiftyRSA in Frameworks */, 1FF1361A2BFBC841006A6101 /* SwiftyAttributes in Frameworks */, 847EFC7236336B67A1A89358 /* libPods-BroadcastUploadExtension.a in Frameworks */, 1F77A5F12AB9A423007B6037 /* SDWebImageSVGKitPlugin in Frameworks */, @@ -1177,13 +1241,13 @@ buildActionMask = 2147483647; files = ( 2CA1CCCA1F17C503002FE6A2 /* AudioToolbox.framework in Frameworks */, - 2CCCD21D2835088F00F076CE /* OpenSSL in Frameworks */, 1FCE3D532C9B5918009C68A9 /* SwiftyGif in Frameworks */, 1F90EFC725FE4BE700F3FA55 /* IntentsUI.framework in Frameworks */, 2CA1CC971F016117002FE6A2 /* Security.framework in Frameworks */, 1F45A1212A01D8BA005FE87D /* SDWebImageSVGKitPlugin in Frameworks */, 1FAB2E7D2AC99326001214EB /* TOCropViewController in Frameworks */, 1FC4B3252CC054BC00D28138 /* UICKeyChainStore in Frameworks */, + 1F899EFF2EF866F500890727 /* SwiftyRSA in Frameworks */, 2C90E5CF1EDF23A00093D85A /* WebKit.framework in Frameworks */, 2C90E5691EDDE13A0093D85A /* UIKit.framework in Frameworks */, 80CDF8C42A8E098900CB57AE /* SwiftUIIntrospect in Frameworks */, @@ -1222,6 +1286,7 @@ 1F35F8FD2AEEDCCB00044BDA /* AudioToolbox.framework in Frameworks */, 1F45A1252A01D8F7005FE87D /* SDWebImageSVGKitPlugin in Frameworks */, 1F35F8F52AEEDA9800044BDA /* SwiftyAttributes in Frameworks */, + 1F899F142EF889E500890727 /* SwiftyRSA in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1235,6 +1300,7 @@ 1FC4B3272CC0562600D28138 /* UICKeyChainStore in Frameworks */, 1F7AE07A29142E62009F72AD /* NextcloudKit in Frameworks */, 1F7AE07D29158878009F72AD /* IntentsUI.framework in Frameworks */, + 1F899F012EF8688D00890727 /* SwiftyRSA in Frameworks */, 1FF1361C2BFBC86A006A6101 /* SwiftyAttributes in Frameworks */, 3FCA62550CD1442D28E8A7C6 /* libPods-NotificationServiceExtension.a in Frameworks */, 1F45A11E2A01D719005FE87D /* SDWebImage in Frameworks */, @@ -1472,7 +1538,6 @@ 1F759C082B63B9A7000534AB /* SDWebImage */, 1F759C0A2B63B9A7000534AB /* SDWebImageSVGKitPlugin */, 1F759C0D2B63B9BA000534AB /* WebRTC */, - 1F759C0F2B63B9D9000534AB /* OpenSSL */, 1F759C152B63B9D9000534AB /* NextcloudKit */, 1F759C172B63B9D9000534AB /* SwiftyAttributes */, 1F759C192B63B9D9000534AB /* CDMarkdownKit */, @@ -1492,6 +1557,7 @@ 1FA93D982D70FCC200DF6CDF /* Sources */, 1FA93D992D70FCC200DF6CDF /* Frameworks */, 1FA93D9A2D70FCC200DF6CDF /* Resources */, + 1F899F1D2EF889F300890727 /* Embed Frameworks */, ); buildRules = ( ); @@ -1514,6 +1580,7 @@ 1FA93DEE2D71012500DF6CDF /* SwiftyAttributes */, 1FA93DF02D71012900DF6CDF /* UICKeyChainStore */, 1F96D76D2D93013C0088331F /* SwiftyXMLParser */, + 1F899F1A2EF889F300890727 /* SwiftyRSA */, ); productName = TalkIntents; productReference = 1FA93D9C2D70FCC200DF6CDF /* TalkIntents.appex */; @@ -1545,6 +1612,7 @@ 1FF2FD572AB99CCB000C9905 /* Sources */, 1FF2FD582AB99CCB000C9905 /* Frameworks */, 1FF2FD592AB99CCB000C9905 /* Resources */, + 1F899F192EF889EE00890727 /* Embed Frameworks */, ); buildRules = ( ); @@ -1567,6 +1635,7 @@ 1FF136192BFBC841006A6101 /* SwiftyAttributes */, 1FC4B32A2CC0564100D28138 /* UICKeyChainStore */, 2C349B852D71A15F004EC882 /* SwiftyXMLParser */, + 1F899F162EF889EE00890727 /* SwiftyRSA */, ); productName = BroadcastUploadExtension; productReference = 1FF2FD5B2AB99CCB000C9905 /* BroadcastUploadExtension.appex */; @@ -1586,6 +1655,7 @@ 1FA93DAA2D70FCC200DF6CDF /* Embed ExtensionKit Extensions */, 2C5E72BC27957FCA004ED7FB /* ShellScript */, C21100AE204AFC213989DA96 /* [CP] Embed Pods Frameworks */, + 1F899F092EF8698200890727 /* Embed Frameworks */, ); buildRules = ( ); @@ -1619,7 +1689,6 @@ ); name = NextcloudTalk; packageProductDependencies = ( - 2CCCD21C2835088F00F076CE /* OpenSSL */, 1F468E7528DCC6C60099597B /* Dynamic */, 1F7AE07729142CA1009F72AD /* NextcloudKit */, 1F66B72E29FABD01003FB168 /* SwiftyAttributes */, @@ -1637,6 +1706,7 @@ 1FC4B3352CC0586A00D28138 /* libPhoneNumber */, 1F549B682D3A9AA500E9AA9E /* DebouncedOnChange */, 2C349B832D71A058004EC882 /* SwiftyXMLParser */, + 1F899EFE2EF866F500890727 /* SwiftyRSA */, ); productName = NextcloudTalk; productReference = 2C05747D1EDD9E8E00D9E7F2 /* NextcloudTalk.app */; @@ -1650,6 +1720,7 @@ 2C62AF9F24C08845007E460A /* Sources */, 2C62AFA024C08845007E460A /* Frameworks */, 2C62AFA124C08845007E460A /* Resources */, + 1F899F072EF8689500890727 /* Embed Frameworks */, ); buildRules = ( ); @@ -1676,6 +1747,7 @@ 1FC4B3282CC0562E00D28138 /* UICKeyChainStore */, 1FC4B3302CC057B700D28138 /* MBProgressHUD */, 2C349B812D71A03B004EC882 /* SwiftyXMLParser */, + 1F899F132EF889E500890727 /* SwiftyRSA */, ); productName = ShareExtension; productReference = 2C62AFA324C08845007E460A /* ShareExtension.appex */; @@ -1689,6 +1761,7 @@ 2CC0014B24A1F0E900A20167 /* Sources */, 2CC0014C24A1F0E900A20167 /* Frameworks */, 2CC0014D24A1F0E900A20167 /* Resources */, + 1F899F032EF8688D00890727 /* Embed Frameworks */, ); buildRules = ( ); @@ -1711,6 +1784,7 @@ 1FF1361B2BFBC86A006A6101 /* SwiftyAttributes */, 1FC4B3262CC0562600D28138 /* UICKeyChainStore */, 2C349B7F2D71A030004EC882 /* SwiftyXMLParser */, + 1F899F002EF8688D00890727 /* SwiftyRSA */, ); productName = NotificationServiceExtension; productReference = 2CC0014F24A1F0E900A20167 /* NotificationServiceExtension.appex */; @@ -1810,7 +1884,6 @@ ); mainGroup = 2C0574741EDD9E8E00D9E7F2; packageReferences = ( - 2CCCD21B2835088F00F076CE /* XCRemoteSwiftPackageReference "OpenSSL" */, 1F468E7428DCC6C60099597B /* XCRemoteSwiftPackageReference "Dynamic" */, 1F7AE07629142CA1009F72AD /* XCRemoteSwiftPackageReference "NextcloudKit" */, 1F66B72D29FABD01003FB168 /* XCRemoteSwiftPackageReference "SwiftyAttributes" */, @@ -1828,6 +1901,7 @@ 1FC4B3342CC0586A00D28138 /* XCRemoteSwiftPackageReference "libPhoneNumber-iOS" */, 1F549B672D3A9AA500E9AA9E /* XCRemoteSwiftPackageReference "DebouncedOnChange" */, 2C349B042D70D360004EC882 /* XCRemoteSwiftPackageReference "SwiftyXMLParser" */, + 1F899EFD2EF866F500890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */, ); preferredProjectObjectVersion = 77; productRefGroup = 2C05747E1EDD9E8E00D9E7F2 /* Products */; @@ -3348,6 +3422,14 @@ version = 2.6.0; }; }; + 1F899EFD2EF866F500890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/TakeScoop/SwiftyRSA"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.8.0; + }; + }; 1FAB2E7B2AC99326001214EB /* XCRemoteSwiftPackageReference "TOCropViewController" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/TimOliver/TOCropViewController"; @@ -3412,14 +3494,6 @@ minimumVersion = 5.6.0; }; }; - 2CCCD21B2835088F00F076CE /* XCRemoteSwiftPackageReference "OpenSSL" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/krzyzanowskim/OpenSSL"; - requirement = { - kind = exactVersion; - version = 3.1.6000; - }; - }; 80CDF8C22A8E098900CB57AE /* XCRemoteSwiftPackageReference "swiftui-introspect" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/siteline/swiftui-introspect"; @@ -3511,11 +3585,6 @@ package = 1FAB2E862ACD44CF001214EB /* XCRemoteSwiftPackageReference "talk-clients-webrtc" */; productName = WebRTC; }; - 1F759C0F2B63B9D9000534AB /* OpenSSL */ = { - isa = XCSwiftPackageProductDependency; - package = 2CCCD21B2835088F00F076CE /* XCRemoteSwiftPackageReference "OpenSSL" */; - productName = OpenSSL; - }; 1F759C152B63B9D9000534AB /* NextcloudKit */ = { isa = XCSwiftPackageProductDependency; package = 1F7AE07629142CA1009F72AD /* XCRemoteSwiftPackageReference "NextcloudKit" */; @@ -3601,6 +3670,31 @@ package = 1F7AE07629142CA1009F72AD /* XCRemoteSwiftPackageReference "NextcloudKit" */; productName = NextcloudKit; }; + 1F899EFE2EF866F500890727 /* SwiftyRSA */ = { + isa = XCSwiftPackageProductDependency; + package = 1F899EFD2EF866F500890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */; + productName = SwiftyRSA; + }; + 1F899F002EF8688D00890727 /* SwiftyRSA */ = { + isa = XCSwiftPackageProductDependency; + package = 1F899EFD2EF866F500890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */; + productName = SwiftyRSA; + }; + 1F899F132EF889E500890727 /* SwiftyRSA */ = { + isa = XCSwiftPackageProductDependency; + package = 1F899EFD2EF866F500890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */; + productName = SwiftyRSA; + }; + 1F899F162EF889EE00890727 /* SwiftyRSA */ = { + isa = XCSwiftPackageProductDependency; + package = 1F899EFD2EF866F500890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */; + productName = SwiftyRSA; + }; + 1F899F1A2EF889F300890727 /* SwiftyRSA */ = { + isa = XCSwiftPackageProductDependency; + package = 1F899EFD2EF866F500890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */; + productName = SwiftyRSA; + }; 1F96D76D2D93013C0088331F /* SwiftyXMLParser */ = { isa = XCSwiftPackageProductDependency; package = 2C349B042D70D360004EC882 /* XCRemoteSwiftPackageReference "SwiftyXMLParser" */; @@ -3731,11 +3825,6 @@ package = 2C349B042D70D360004EC882 /* XCRemoteSwiftPackageReference "SwiftyXMLParser" */; productName = SwiftyXMLParser; }; - 2CCCD21C2835088F00F076CE /* OpenSSL */ = { - isa = XCSwiftPackageProductDependency; - package = 2CCCD21B2835088F00F076CE /* XCRemoteSwiftPackageReference "OpenSSL" */; - productName = OpenSSL; - }; 80CDF8C32A8E098900CB57AE /* SwiftUIIntrospect */ = { isa = XCSwiftPackageProductDependency; package = 80CDF8C22A8E098900CB57AE /* XCRemoteSwiftPackageReference "swiftui-introspect" */; diff --git a/NextcloudTalk/AppDelegate.m b/NextcloudTalk/AppDelegate.m index de70e3049..7932c1232 100644 --- a/NextcloudTalk/AppDelegate.m +++ b/NextcloudTalk/AppDelegate.m @@ -24,7 +24,6 @@ #import "NCNavigationController.h" #import "NCNotificationController.h" #import "NCPushNotification.h" -#import "NCPushNotificationsUtils.h" #import "NCRoomsManager.h" #import "NCSettingsController.h" #import "NCUserInterfaceController.h" @@ -347,7 +346,7 @@ - (void)application:(UIApplication *)application didReceiveRemoteNotification:(N for (TalkAccount *account in [[NCDatabaseManager sharedInstance] allAccounts]) { NSData *pushNotificationPrivateKey = [[NCKeyChainController sharedInstance] pushNotificationPrivateKeyForAccountId:account.accountId]; if (message && pushNotificationPrivateKey) { - NSString *decryptedMessage = [NCPushNotificationsUtils decryptPushNotification:message withDevicePrivateKey:pushNotificationPrivateKey]; + NSString *decryptedMessage = [NCPushNotificationsUtils decryptPushNotificationWithMessage:message withDevicePrivateKey:pushNotificationPrivateKey]; if (decryptedMessage) { NCPushNotification *pushNotification = [NCPushNotification pushNotificationFromDecryptedString:decryptedMessage withAccountId:account.accountId]; [[NCNotificationController sharedInstance] processBackgroundPushNotification:pushNotification]; @@ -389,7 +388,7 @@ - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayloa continue; } - NSString *decryptedMessage = [NCPushNotificationsUtils decryptPushNotification:message withDevicePrivateKey:pushNotificationPrivateKey]; + NSString *decryptedMessage = [NCPushNotificationsUtils decryptPushNotificationWithMessage:message withDevicePrivateKey:pushNotificationPrivateKey]; if (!decryptedMessage) { continue; diff --git a/NextcloudTalk/Notifications/NCPushNotificationKeyPair.swift b/NextcloudTalk/Notifications/NCPushNotificationKeyPair.swift new file mode 100644 index 000000000..cb69fc4f5 --- /dev/null +++ b/NextcloudTalk/Notifications/NCPushNotificationKeyPair.swift @@ -0,0 +1,16 @@ +// +// SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors +// SPDX-License-Identifier: GPL-3.0-or-later +// + +@objcMembers +public class NCPushNotificationKeyPair: NSObject { + + public var privateKey: Data + public var publicKey: Data + + init(privateKey: Data, publicKey: Data) { + self.privateKey = privateKey + self.publicKey = publicKey + } +} diff --git a/NextcloudTalk/Notifications/NCPushNotificationsUtils.h b/NextcloudTalk/Notifications/NCPushNotificationsUtils.h deleted file mode 100644 index 309d7e4a2..000000000 --- a/NextcloudTalk/Notifications/NCPushNotificationsUtils.h +++ /dev/null @@ -1,16 +0,0 @@ -/** - * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface NCPushNotificationsUtils : NSObject - -+ (NSString *)decryptPushNotification:(NSString *)message withDevicePrivateKey:(NSData *)privateKey; - -@end - -NS_ASSUME_NONNULL_END diff --git a/NextcloudTalk/Notifications/NCPushNotificationsUtils.m b/NextcloudTalk/Notifications/NCPushNotificationsUtils.m deleted file mode 100644 index d88243ec9..000000000 --- a/NextcloudTalk/Notifications/NCPushNotificationsUtils.m +++ /dev/null @@ -1,53 +0,0 @@ -/** - * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#import "NCPushNotificationsUtils.h" - -#import -#import -#import -#import -#import -#import - -@implementation NCPushNotificationsUtils - -+ (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; -} - -@end diff --git a/NextcloudTalk/Notifications/NCPushNotificationsUtils.swift b/NextcloudTalk/Notifications/NCPushNotificationsUtils.swift new file mode 100644 index 000000000..8411dfb72 --- /dev/null +++ b/NextcloudTalk/Notifications/NCPushNotificationsUtils.swift @@ -0,0 +1,43 @@ +// +// SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors +// SPDX-License-Identifier: GPL-3.0-or-later +// + +import SwiftyRSA + +@objcMembers +public class NCPushNotificationsUtils: NSObject { + + public static func decryptPushNotification(message: String, withDevicePrivateKey key: NSData) -> String? { + do { + let privateKey = try PrivateKey(pemEncoded: String(data: key as Data, encoding: .utf8)!) + let encryptedMessage = try EncryptedMessage(base64Encoded: message) + let clearMessage = try encryptedMessage.decrypted(with: privateKey, padding: .PKCS1) + + return try clearMessage.string(encoding: .utf8) + } catch { + print("decryptPushNotificationError: \(error)") + } + + return nil + } + + public static func generatePushNotificationKeyPair() -> NCPushNotificationKeyPair? { + do { + let keyPair = try SwiftyRSA.generateRSAKeyPair(sizeInBits: 2048) + + let privateKey = try keyPair.privateKey.data().prependx509Header().base64EncodedString(options: [.lineLength64Characters, .endLineWithLineFeed]) + let publicKey = try keyPair.publicKey.data().prependx509Header().base64EncodedString(options: [.lineLength64Characters, .endLineWithLineFeed]) + + let privateKeyPem = "-----BEGIN PRIVATE KEY-----\n\(privateKey)\n-----END PRIVATE KEY-----" + let publicKeyPem = "-----BEGIN PUBLIC KEY-----\n\(publicKey)\n-----END PUBLIC KEY-----" + + return NCPushNotificationKeyPair(privateKey: privateKeyPem.data(using: .utf8)!, publicKey: publicKeyPem.data(using: .utf8)!) + } catch { + NCUtils.log("Error generating push keypair: \(error)") + } + + return nil + } + +} diff --git a/NextcloudTalk/Security/CCCertificate.m b/NextcloudTalk/Security/CCCertificate.m index e50fd4efb..affea659b 100755 --- a/NextcloudTalk/Security/CCCertificate.m +++ b/NextcloudTalk/Security/CCCertificate.m @@ -5,13 +5,10 @@ #import "CCCertificate.h" -#import -#import -#import -#import - #import "NCAppBranding.h" +#import "NextcloudTalk-Swift.h" + @implementation CCCertificate NSString *const appCertificates = @"Library/Application Support/Certificates"; @@ -79,45 +76,23 @@ - (BOOL)checkTrustedChallenge:(NSURLAuthenticationChallenge *)challenge - (void)saveCertificate:(SecTrustRef)trust withName:(NSString *)certName { SecCertificateRef currentServerCert = SecTrustGetLeafCertificate(trust); - CFDataRef data = SecCertificateCopyData(currentServerCert); - X509 *x509cert = NULL; - if (data) { - BIO *mem = BIO_new_mem_buf((void *)CFDataGetBytePtr(data), (int)CFDataGetLength(data)); - x509cert = d2i_X509_bio(mem, NULL); - BIO_free(mem); - CFRelease(data); - - if (!x509cert) { - - NSLog(@"[LOG] OpenSSL couldn't parse X509 Certificate"); - - } else { - - NSString *localCertificatesFolder = [self getDirectoryCerificates]; - - certName = [NSString stringWithFormat:@"%@/%@",localCertificatesFolder,certName]; - - if ([[NSFileManager defaultManager] fileExistsAtPath:certName]) { - NSError *error; - [[NSFileManager defaultManager] removeItemAtPath:certName error:&error]; - } - - FILE *file; - file = fopen([certName UTF8String], "w"); - if (file) { - PEM_write_X509(file, x509cert); - } - fclose(file); - } - - } else { - - NSLog(@"[LOG] Failed to retrieve DER data from Certificate Ref"); + + if (!data) { + return; } - - //Free - X509_free(x509cert); + + NSData *nsData = (__bridge NSData *)(data); + NSString *base64Certificate = [nsData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength | NSDataBase64EncodingEndLineWithLineFeed]; + + if (!base64Certificate) { + return; + } + + NSString *localCertificatesFolder = [self getDirectoryCerificates]; + certName = [NSString stringWithFormat:@"%@/%@", localCertificatesFolder, certName]; + + [base64Certificate writeToFile:certName atomically:YES encoding:NSUTF8StringEncoding error:nil]; } - (void)presentViewControllerCertificateWithTitle:(NSString *)title viewController:(UIViewController *)viewController delegate:(id)delegate diff --git a/NextcloudTalk/Settings/NCSettingsController.h b/NextcloudTalk/Settings/NCSettingsController.h index c9035a54d..7df2079b5 100644 --- a/NextcloudTalk/Settings/NCSettingsController.h +++ b/NextcloudTalk/Settings/NCSettingsController.h @@ -66,7 +66,6 @@ typedef NS_ENUM(NSInteger, NCPreferredFileSorting) { - (void)connectDisconnectedExternalSignalingControllers; - (void)disconnectAllExternalSignalingControllers; - (void)subscribeForPushNotificationsForAccountId:(NSString *)accountId withCompletionBlock:(SubscribeForPushNotificationsCompletionBlock)block; -- (NSInteger)chatMaxLengthConfigCapability; - (BOOL)canCreateGroupAndPublicRooms; - (BOOL)isGuestsAppEnabled; - (BOOL)isReferenceApiSupported; diff --git a/NextcloudTalk/Settings/NCSettingsController.m b/NextcloudTalk/Settings/NCSettingsController.m index a07f5ea58..faefa488b 100644 --- a/NextcloudTalk/Settings/NCSettingsController.m +++ b/NextcloudTalk/Settings/NCSettingsController.m @@ -7,13 +7,6 @@ @import NextcloudKit; -#import -#import -#import -#import -#import -#import - #import "JDStatusBarNotification.h" #import "NCAPIController.h" @@ -30,18 +23,6 @@ NSString * const NCSettingsControllerDidChangeActiveAccountNotification = @"NCSettingsControllerDidChangeActiveAccountNotification"; -@interface NCPushNotificationKeyPair : NSObject - -@property (nonatomic, copy) NSData *publicKey; -@property (nonatomic, copy) NSData *privateKey; - -@end - -@implementation NCPushNotificationKeyPair - -@end - - @implementation NCSettingsController NSString * const kUserProfileUserId = @"id"; @@ -740,13 +721,11 @@ - (void)subscribeForPushNotificationsForAccountId:(NSString *)accountId withComp NSData *pushNotificationPrivateKey = [[NCKeyChainController sharedInstance] pushNotificationPrivateKeyForAccountId:accountId]; if (pushNotificationPublicKey && pushNotificationPrivateKey) { - keyPair = [[NCPushNotificationKeyPair alloc] init]; - keyPair.publicKey = pushNotificationPublicKey; - keyPair.privateKey = pushNotificationPrivateKey; + keyPair = [[NCPushNotificationKeyPair alloc] initWithPrivateKey:pushNotificationPrivateKey publicKey:pushNotificationPublicKey]; } else { - keyPair = [self generatePushNotificationsKeyPairForAccountId:accountId]; + keyPair = [NCPushNotificationsUtils generatePushNotificationKeyPair]; } - + if (!keyPair) { [NCUtils log:@"Error while subscribing: Unable to generate push notifications key pair."]; @@ -847,77 +826,4 @@ - (void)subscribeForPushNotificationsForAccountId:(NSString *)accountId withComp #endif } -- (NCPushNotificationKeyPair *)generatePushNotificationsKeyPairForAccountId:(NSString *)accountId -{ - EVP_PKEY *pkey = [self generateRSAKey]; - if (!pkey) { - return nil; - } - - // Extract publicKey, privateKey - int len; - char *keyBytes; - - // PublicKey - BIO *publicKeyBIO = BIO_new(BIO_s_mem()); - PEM_write_bio_PUBKEY(publicKeyBIO, pkey); - - len = BIO_pending(publicKeyBIO); - keyBytes = malloc(len); - - BIO_read(publicKeyBIO, keyBytes, len); - NSData *pnPublicKey = [NSData dataWithBytes:keyBytes length:len]; - NSLog(@"Push Notifications Key Pair generated: \n%@", [[NSString alloc] initWithData:pnPublicKey encoding:NSUTF8StringEncoding]); - - // PrivateKey - BIO *privateKeyBIO = BIO_new(BIO_s_mem()); - PEM_write_bio_PKCS8PrivateKey(privateKeyBIO, pkey, NULL, NULL, 0, NULL, NULL); - - len = BIO_pending(privateKeyBIO); - keyBytes = malloc(len); - - BIO_read(privateKeyBIO, keyBytes, len); - NSData *pnPrivateKey = [NSData dataWithBytes:keyBytes length:len]; - EVP_PKEY_free(pkey); - - NCPushNotificationKeyPair *keyPair = [[NCPushNotificationKeyPair alloc] init]; - keyPair.publicKey = pnPublicKey; - keyPair.privateKey = pnPrivateKey; - - return keyPair; -} - -- (EVP_PKEY *)generateRSAKey -{ - EVP_PKEY *pkey = EVP_PKEY_new(); - if (!pkey) { - return NULL; - } - - BIGNUM *bigNumber = BN_new(); - int exponent = RSA_F4; - RSA *rsa = RSA_new(); - - if (BN_set_word(bigNumber, exponent) == 0) { - pkey = NULL; - goto cleanup; - } - - if (RSA_generate_key_ex(rsa, 2048, bigNumber, NULL) == 0) { - pkey = NULL; - goto cleanup; - } - - if (!EVP_PKEY_set1_RSA(pkey, rsa)) { - pkey = NULL; - goto cleanup; - } - -cleanup: - RSA_free(rsa); - BN_free(bigNumber); - - return pkey; -} - @end diff --git a/NotificationServiceExtension/NotificationService.m b/NotificationServiceExtension/NotificationService.m index 84f68a531..34d0ace60 100644 --- a/NotificationServiceExtension/NotificationService.m +++ b/NotificationServiceExtension/NotificationService.m @@ -12,7 +12,6 @@ #import "NCKeyChainController.h" #import "NCNotification.h" #import "NCPushNotification.h" -#import "NCPushNotificationsUtils.h" #import "NextcloudTalk-Swift.h" @@ -91,7 +90,7 @@ - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withConte NSData *pushNotificationPrivateKey = [[NCKeyChainController sharedInstance] pushNotificationPrivateKeyForAccountId:account.accountId]; if (message && pushNotificationPrivateKey) { @try { - NSString *decryptedMessage = [NCPushNotificationsUtils decryptPushNotification:message withDevicePrivateKey:pushNotificationPrivateKey]; + NSString *decryptedMessage = [NCPushNotificationsUtils decryptPushNotificationWithMessage:message withDevicePrivateKey:pushNotificationPrivateKey]; if (decryptedMessage) { NCPushNotification *pushNotification = [NCPushNotification pushNotificationFromDecryptedString:decryptedMessage withAccountId:account.accountId]; From 50fb88c0bf3f6b65e4ce6df46d17f7155405f9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Tue, 23 Dec 2025 13:36:23 +0100 Subject: [PATCH 2/2] chore: Use updated SwityRSA library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- NextcloudTalk.xcodeproj/project.pbxproj | 73 ++--- NextcloudTalk/AppDelegate.m | 49 +-- NextcloudTalk/Database/TalkAccount.h | 2 +- .../NCPushNotificationsUtils.swift | 29 +- NextcloudTalk/Settings/NCKeyChainController.h | 4 +- .../NotificationService.m | 310 +++++++++--------- 6 files changed, 241 insertions(+), 226 deletions(-) diff --git a/NextcloudTalk.xcodeproj/project.pbxproj b/NextcloudTalk.xcodeproj/project.pbxproj index d29c71252..55f58fa2d 100644 --- a/NextcloudTalk.xcodeproj/project.pbxproj +++ b/NextcloudTalk.xcodeproj/project.pbxproj @@ -60,16 +60,12 @@ 1F7AE07C29142E6A009F72AD /* NextcloudKit in Frameworks */ = {isa = PBXBuildFile; productRef = 1F7AE07B29142E6A009F72AD /* NextcloudKit */; }; 1F7AE07D29158878009F72AD /* IntentsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F90EFC225FE489B00F3FA55 /* IntentsUI.framework */; }; 1F8848122A75B68D00063860 /* IntentsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F90EFC225FE489B00F3FA55 /* IntentsUI.framework */; }; - 1F899EFF2EF866F500890727 /* SwiftyRSA in Frameworks */ = {isa = PBXBuildFile; productRef = 1F899EFE2EF866F500890727 /* SwiftyRSA */; }; - 1F899F012EF8688D00890727 /* SwiftyRSA in Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F002EF8688D00890727 /* SwiftyRSA */; }; - 1F899F022EF8688D00890727 /* SwiftyRSA in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F002EF8688D00890727 /* SwiftyRSA */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 1F899F082EF8698200890727 /* SwiftyRSA in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 1F899EFE2EF866F500890727 /* SwiftyRSA */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 1F899F142EF889E500890727 /* SwiftyRSA in Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F132EF889E500890727 /* SwiftyRSA */; }; - 1F899F152EF889E500890727 /* SwiftyRSA in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F132EF889E500890727 /* SwiftyRSA */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 1F899F172EF889EE00890727 /* SwiftyRSA in Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F162EF889EE00890727 /* SwiftyRSA */; }; - 1F899F182EF889EE00890727 /* SwiftyRSA in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F162EF889EE00890727 /* SwiftyRSA */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 1F899F1B2EF889F300890727 /* SwiftyRSA in Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F1A2EF889F300890727 /* SwiftyRSA */; }; - 1F899F1C2EF889F300890727 /* SwiftyRSA in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F1A2EF889F300890727 /* SwiftyRSA */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 1F899F212EF9E5FF00890727 /* SwiftyRSA in Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F202EF9E5FF00890727 /* SwiftyRSA */; }; + 1F899F232EF9E60500890727 /* SwiftyRSA in Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F222EF9E60500890727 /* SwiftyRSA */; }; + 1F899F252EF9E60900890727 /* SwiftyRSA in Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F242EF9E60900890727 /* SwiftyRSA */; }; + 1F899F272EF9E60F00890727 /* SwiftyRSA in Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F262EF9E60F00890727 /* SwiftyRSA */; }; + 1F899F292EF9E61300890727 /* SwiftyRSA in Frameworks */ = {isa = PBXBuildFile; productRef = 1F899F282EF9E61300890727 /* SwiftyRSA */; }; + 1F89A8442EFAC35D00890727 /* SwiftyRSA in Frameworks */ = {isa = PBXBuildFile; productRef = 1F89A8432EFAC35D00890727 /* SwiftyRSA */; }; 1F90EFC725FE4BE700F3FA55 /* IntentsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F90EFC225FE489B00F3FA55 /* IntentsUI.framework */; }; 1F96D76E2D93013C0088331F /* SwiftyXMLParser in Frameworks */ = {isa = PBXBuildFile; productRef = 1F96D76D2D93013C0088331F /* SwiftyXMLParser */; }; 1FA93DA52D70FCC200DF6CDF /* TalkIntents.appex in Embed ExtensionKit Extensions */ = {isa = PBXBuildFile; fileRef = 1FA93D9C2D70FCC200DF6CDF /* TalkIntents.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -213,7 +209,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 1F899F022EF8688D00890727 /* SwiftyRSA in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -224,7 +219,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 1F899F152EF889E500890727 /* SwiftyRSA in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -235,7 +229,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 1F899F082EF8698200890727 /* SwiftyRSA in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -246,7 +239,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 1F899F182EF889EE00890727 /* SwiftyRSA in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -257,7 +249,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 1F899F1C2EF889F300890727 /* SwiftyRSA in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -1201,7 +1192,7 @@ 1FA93DE92D71011800DF6CDF /* Realm in Frameworks */, 1FA93DE72D71011400DF6CDF /* NextcloudKit in Frameworks */, 1FA93DF12D71012900DF6CDF /* UICKeyChainStore in Frameworks */, - 1F899F1B2EF889F300890727 /* SwiftyRSA in Frameworks */, + 1F899F292EF9E61300890727 /* SwiftyRSA in Frameworks */, 1FA93DEB2D71011C00DF6CDF /* SDWebImage in Frameworks */, 1FA93DE52D71010F00DF6CDF /* CDMarkdownKit in Frameworks */, 1FA93DED2D71012000DF6CDF /* SDWebImageSVGKitPlugin in Frameworks */, @@ -1228,7 +1219,7 @@ 1FC4B32B2CC0564100D28138 /* UICKeyChainStore in Frameworks */, 1FF2FD5D2AB99CCB000C9905 /* ReplayKit.framework in Frameworks */, 1F77A5EF2AB9A41E007B6037 /* SDWebImage in Frameworks */, - 1F899F172EF889EE00890727 /* SwiftyRSA in Frameworks */, + 1F899F272EF9E60F00890727 /* SwiftyRSA in Frameworks */, 1FF1361A2BFBC841006A6101 /* SwiftyAttributes in Frameworks */, 847EFC7236336B67A1A89358 /* libPods-BroadcastUploadExtension.a in Frameworks */, 1F77A5F12AB9A423007B6037 /* SDWebImageSVGKitPlugin in Frameworks */, @@ -1241,13 +1232,14 @@ buildActionMask = 2147483647; files = ( 2CA1CCCA1F17C503002FE6A2 /* AudioToolbox.framework in Frameworks */, + 1F899F212EF9E5FF00890727 /* SwiftyRSA in Frameworks */, 1FCE3D532C9B5918009C68A9 /* SwiftyGif in Frameworks */, 1F90EFC725FE4BE700F3FA55 /* IntentsUI.framework in Frameworks */, 2CA1CC971F016117002FE6A2 /* Security.framework in Frameworks */, 1F45A1212A01D8BA005FE87D /* SDWebImageSVGKitPlugin in Frameworks */, 1FAB2E7D2AC99326001214EB /* TOCropViewController in Frameworks */, 1FC4B3252CC054BC00D28138 /* UICKeyChainStore in Frameworks */, - 1F899EFF2EF866F500890727 /* SwiftyRSA in Frameworks */, + 1F89A8442EFAC35D00890727 /* SwiftyRSA in Frameworks */, 2C90E5CF1EDF23A00093D85A /* WebKit.framework in Frameworks */, 2C90E5691EDDE13A0093D85A /* UIKit.framework in Frameworks */, 80CDF8C42A8E098900CB57AE /* SwiftUIIntrospect in Frameworks */, @@ -1286,7 +1278,7 @@ 1F35F8FD2AEEDCCB00044BDA /* AudioToolbox.framework in Frameworks */, 1F45A1252A01D8F7005FE87D /* SDWebImageSVGKitPlugin in Frameworks */, 1F35F8F52AEEDA9800044BDA /* SwiftyAttributes in Frameworks */, - 1F899F142EF889E500890727 /* SwiftyRSA in Frameworks */, + 1F899F252EF9E60900890727 /* SwiftyRSA in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1300,7 +1292,7 @@ 1FC4B3272CC0562600D28138 /* UICKeyChainStore in Frameworks */, 1F7AE07A29142E62009F72AD /* NextcloudKit in Frameworks */, 1F7AE07D29158878009F72AD /* IntentsUI.framework in Frameworks */, - 1F899F012EF8688D00890727 /* SwiftyRSA in Frameworks */, + 1F899F232EF9E60500890727 /* SwiftyRSA in Frameworks */, 1FF1361C2BFBC86A006A6101 /* SwiftyAttributes in Frameworks */, 3FCA62550CD1442D28E8A7C6 /* libPods-NotificationServiceExtension.a in Frameworks */, 1F45A11E2A01D719005FE87D /* SDWebImage in Frameworks */, @@ -1580,7 +1572,7 @@ 1FA93DEE2D71012500DF6CDF /* SwiftyAttributes */, 1FA93DF02D71012900DF6CDF /* UICKeyChainStore */, 1F96D76D2D93013C0088331F /* SwiftyXMLParser */, - 1F899F1A2EF889F300890727 /* SwiftyRSA */, + 1F899F282EF9E61300890727 /* SwiftyRSA */, ); productName = TalkIntents; productReference = 1FA93D9C2D70FCC200DF6CDF /* TalkIntents.appex */; @@ -1635,7 +1627,7 @@ 1FF136192BFBC841006A6101 /* SwiftyAttributes */, 1FC4B32A2CC0564100D28138 /* UICKeyChainStore */, 2C349B852D71A15F004EC882 /* SwiftyXMLParser */, - 1F899F162EF889EE00890727 /* SwiftyRSA */, + 1F899F262EF9E60F00890727 /* SwiftyRSA */, ); productName = BroadcastUploadExtension; productReference = 1FF2FD5B2AB99CCB000C9905 /* BroadcastUploadExtension.appex */; @@ -1706,7 +1698,8 @@ 1FC4B3352CC0586A00D28138 /* libPhoneNumber */, 1F549B682D3A9AA500E9AA9E /* DebouncedOnChange */, 2C349B832D71A058004EC882 /* SwiftyXMLParser */, - 1F899EFE2EF866F500890727 /* SwiftyRSA */, + 1F899F202EF9E5FF00890727 /* SwiftyRSA */, + 1F89A8432EFAC35D00890727 /* SwiftyRSA */, ); productName = NextcloudTalk; productReference = 2C05747D1EDD9E8E00D9E7F2 /* NextcloudTalk.app */; @@ -1747,7 +1740,7 @@ 1FC4B3282CC0562E00D28138 /* UICKeyChainStore */, 1FC4B3302CC057B700D28138 /* MBProgressHUD */, 2C349B812D71A03B004EC882 /* SwiftyXMLParser */, - 1F899F132EF889E500890727 /* SwiftyRSA */, + 1F899F242EF9E60900890727 /* SwiftyRSA */, ); productName = ShareExtension; productReference = 2C62AFA324C08845007E460A /* ShareExtension.appex */; @@ -1784,7 +1777,7 @@ 1FF1361B2BFBC86A006A6101 /* SwiftyAttributes */, 1FC4B3262CC0562600D28138 /* UICKeyChainStore */, 2C349B7F2D71A030004EC882 /* SwiftyXMLParser */, - 1F899F002EF8688D00890727 /* SwiftyRSA */, + 1F899F222EF9E60500890727 /* SwiftyRSA */, ); productName = NotificationServiceExtension; productReference = 2CC0014F24A1F0E900A20167 /* NotificationServiceExtension.appex */; @@ -1901,7 +1894,7 @@ 1FC4B3342CC0586A00D28138 /* XCRemoteSwiftPackageReference "libPhoneNumber-iOS" */, 1F549B672D3A9AA500E9AA9E /* XCRemoteSwiftPackageReference "DebouncedOnChange" */, 2C349B042D70D360004EC882 /* XCRemoteSwiftPackageReference "SwiftyXMLParser" */, - 1F899EFD2EF866F500890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */, + 1F89A8422EFAC35D00890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */, ); preferredProjectObjectVersion = 77; productRefGroup = 2C05747E1EDD9E8E00D9E7F2 /* Products */; @@ -3422,12 +3415,12 @@ version = 2.6.0; }; }; - 1F899EFD2EF866F500890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */ = { + 1F89A8422EFAC35D00890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/TakeScoop/SwiftyRSA"; + repositoryURL = "https://github.com/nextcloud-deps/SwiftyRSA"; requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.8.0; + kind = revision; + revision = 36f9963a83a602bd2ea57ff3ccf600d6284d91e4; }; }; 1FAB2E7B2AC99326001214EB /* XCRemoteSwiftPackageReference "TOCropViewController" */ = { @@ -3670,29 +3663,29 @@ package = 1F7AE07629142CA1009F72AD /* XCRemoteSwiftPackageReference "NextcloudKit" */; productName = NextcloudKit; }; - 1F899EFE2EF866F500890727 /* SwiftyRSA */ = { + 1F899F202EF9E5FF00890727 /* SwiftyRSA */ = { + isa = XCSwiftPackageProductDependency; + productName = SwiftyRSA; + }; + 1F899F222EF9E60500890727 /* SwiftyRSA */ = { isa = XCSwiftPackageProductDependency; - package = 1F899EFD2EF866F500890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */; productName = SwiftyRSA; }; - 1F899F002EF8688D00890727 /* SwiftyRSA */ = { + 1F899F242EF9E60900890727 /* SwiftyRSA */ = { isa = XCSwiftPackageProductDependency; - package = 1F899EFD2EF866F500890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */; productName = SwiftyRSA; }; - 1F899F132EF889E500890727 /* SwiftyRSA */ = { + 1F899F262EF9E60F00890727 /* SwiftyRSA */ = { isa = XCSwiftPackageProductDependency; - package = 1F899EFD2EF866F500890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */; productName = SwiftyRSA; }; - 1F899F162EF889EE00890727 /* SwiftyRSA */ = { + 1F899F282EF9E61300890727 /* SwiftyRSA */ = { isa = XCSwiftPackageProductDependency; - package = 1F899EFD2EF866F500890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */; productName = SwiftyRSA; }; - 1F899F1A2EF889F300890727 /* SwiftyRSA */ = { + 1F89A8432EFAC35D00890727 /* SwiftyRSA */ = { isa = XCSwiftPackageProductDependency; - package = 1F899EFD2EF866F500890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */; + package = 1F89A8422EFAC35D00890727 /* XCRemoteSwiftPackageReference "SwiftyRSA" */; productName = SwiftyRSA; }; 1F96D76D2D93013C0088331F /* SwiftyXMLParser */ = { diff --git a/NextcloudTalk/AppDelegate.m b/NextcloudTalk/AppDelegate.m index 7932c1232..92e9b6f38 100644 --- a/NextcloudTalk/AppDelegate.m +++ b/NextcloudTalk/AppDelegate.m @@ -343,16 +343,19 @@ - (void)application:(UIApplication *)application didReceiveRemoteNotification:(N { // Called when a background notification is delivered. NSString *message = [userInfo objectForKey:@"subject"]; + NSString *signature = [userInfo objectForKey:@"signature"]; + + if (!message || !signature) { + return; + } + for (TalkAccount *account in [[NCDatabaseManager sharedInstance] allAccounts]) { - NSData *pushNotificationPrivateKey = [[NCKeyChainController sharedInstance] pushNotificationPrivateKeyForAccountId:account.accountId]; - if (message && pushNotificationPrivateKey) { - NSString *decryptedMessage = [NCPushNotificationsUtils decryptPushNotificationWithMessage:message withDevicePrivateKey:pushNotificationPrivateKey]; - if (decryptedMessage) { - NCPushNotification *pushNotification = [NCPushNotification pushNotificationFromDecryptedString:decryptedMessage withAccountId:account.accountId]; - [[NCNotificationController sharedInstance] processBackgroundPushNotification:pushNotification]; - - break; - } + NSString *decryptedMessage = [NCPushNotificationsUtils decryptPushNotificationWithMessageBase64:message withSignatureBase64:signature forAccount:account]; + if (decryptedMessage) { + NCPushNotification *pushNotification = [NCPushNotification pushNotificationFromDecryptedString:decryptedMessage withAccountId:account.accountId]; + [[NCNotificationController sharedInstance] processBackgroundPushNotification:pushNotification]; + + break; } } @@ -381,25 +384,23 @@ - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayloa [NCUtils log:@"Received PushKit notification"]; NSString *message = [payload.dictionaryPayload objectForKey:@"subject"]; - for (TalkAccount *account in [[NCDatabaseManager sharedInstance] allAccounts]) { - NSData *pushNotificationPrivateKey = [[NCKeyChainController sharedInstance] pushNotificationPrivateKeyForAccountId:account.accountId]; + NSString *signature = [payload.dictionaryPayload objectForKey:@"signature"]; - if (!message || !pushNotificationPrivateKey) { - continue; - } - - NSString *decryptedMessage = [NCPushNotificationsUtils decryptPushNotificationWithMessage:message withDevicePrivateKey:pushNotificationPrivateKey]; + if (message && signature) { + for (TalkAccount *account in [[NCDatabaseManager sharedInstance] allAccounts]) { + NSString *decryptedMessage = [NCPushNotificationsUtils decryptPushNotificationWithMessageBase64:message withSignatureBase64:signature forAccount:account]; - if (!decryptedMessage) { - continue; - } + if (!decryptedMessage) { + continue; + } - NCPushNotification *pushNotification = [NCPushNotification pushNotificationFromDecryptedString:decryptedMessage withAccountId:account.accountId]; + NCPushNotification *pushNotification = [NCPushNotification pushNotificationFromDecryptedString:decryptedMessage withAccountId:account.accountId]; - if ( pushNotification && pushNotification.type == NCPushNotificationTypeCall) { - [[NCNotificationController sharedInstance] showIncomingCallForPushNotification:pushNotification]; - completion(); - return; + if (pushNotification && pushNotification.type == NCPushNotificationTypeCall) { + [[NCNotificationController sharedInstance] showIncomingCallForPushNotification:pushNotification]; + completion(); + return; + } } } diff --git a/NextcloudTalk/Database/TalkAccount.h b/NextcloudTalk/Database/TalkAccount.h index 62e7520dc..77d8d990f 100644 --- a/NextcloudTalk/Database/TalkAccount.h +++ b/NextcloudTalk/Database/TalkAccount.h @@ -29,7 +29,7 @@ NS_ASSUME_NONNULL_BEGIN @property NSInteger lastPushSubscription; @property NSString *deviceIdentifier; @property NSString *deviceSignature; -@property NSString *userPublicKey; +@property NSString * _Nullable userPublicKey; @property NSInteger unreadBadgeNumber; @property BOOL unreadNotification; @property NSInteger lastContactSync; diff --git a/NextcloudTalk/Notifications/NCPushNotificationsUtils.swift b/NextcloudTalk/Notifications/NCPushNotificationsUtils.swift index 8411dfb72..de19e80c7 100644 --- a/NextcloudTalk/Notifications/NCPushNotificationsUtils.swift +++ b/NextcloudTalk/Notifications/NCPushNotificationsUtils.swift @@ -8,11 +8,25 @@ import SwiftyRSA @objcMembers public class NCPushNotificationsUtils: NSObject { - public static func decryptPushNotification(message: String, withDevicePrivateKey key: NSData) -> String? { + public static func decryptPushNotification(withMessageBase64 messageBase64: String, withSignatureBase64 signatureBase64: String, forAccount account: TalkAccount) -> String? { do { - let privateKey = try PrivateKey(pemEncoded: String(data: key as Data, encoding: .utf8)!) - let encryptedMessage = try EncryptedMessage(base64Encoded: message) - let clearMessage = try encryptedMessage.decrypted(with: privateKey, padding: .PKCS1) + guard let userPublicKeyPem = account.userPublicKey else { return nil } + + let encryptedMessage = try EncryptedMessage(base64Encoded: messageBase64) + let userPublicKey = try RsaPublicKey(pemEncoded: userPublicKeyPem) + let signature = try Signature(base64Encoded: signatureBase64) + + guard try encryptedMessage.verify(with: userPublicKey, signature: signature, digestType: .sha512) else { + return nil + } + + guard let devicePrivateKeyData = NCKeyChainController.sharedInstance().pushNotificationPrivateKey(forAccountId: account.accountId), + let devicePrivateKeyPem = String(data: devicePrivateKeyData, encoding: .utf8) else { + return nil + } + + let devicePrivateKey = try RsaPrivateKey(pemEncoded: devicePrivateKeyPem) + let clearMessage = try encryptedMessage.decrypted(with: devicePrivateKey, padding: .PKCS1) return try clearMessage.string(encoding: .utf8) } catch { @@ -26,11 +40,8 @@ public class NCPushNotificationsUtils: NSObject { do { let keyPair = try SwiftyRSA.generateRSAKeyPair(sizeInBits: 2048) - let privateKey = try keyPair.privateKey.data().prependx509Header().base64EncodedString(options: [.lineLength64Characters, .endLineWithLineFeed]) - let publicKey = try keyPair.publicKey.data().prependx509Header().base64EncodedString(options: [.lineLength64Characters, .endLineWithLineFeed]) - - let privateKeyPem = "-----BEGIN PRIVATE KEY-----\n\(privateKey)\n-----END PRIVATE KEY-----" - let publicKeyPem = "-----BEGIN PUBLIC KEY-----\n\(publicKey)\n-----END PUBLIC KEY-----" + let privateKeyPem = try keyPair.privateKey.pemStringPkcs8() + let publicKeyPem = try keyPair.publicKey.pemStringPkcs8() return NCPushNotificationKeyPair(privateKey: privateKeyPem.data(using: .utf8)!, publicKey: publicKeyPem.data(using: .utf8)!) } catch { diff --git a/NextcloudTalk/Settings/NCKeyChainController.h b/NextcloudTalk/Settings/NCKeyChainController.h index fe0813ed0..59ced5d67 100644 --- a/NextcloudTalk/Settings/NCKeyChainController.h +++ b/NextcloudTalk/Settings/NCKeyChainController.h @@ -20,9 +20,9 @@ extern NSString * const kNCPushKitTokenKey; - (void)setToken:(NSString *)token forAccountId:(NSString *)accountId; - (NSString *)tokenForAccountId:(NSString *)accountId; - (void)setPushNotificationPublicKey:(NSData *)privateKey forAccountId:(NSString *)accountId; -- (NSData *)pushNotificationPublicKeyForAccountId:(NSString *)accountId; +- (NSData * _Nullable)pushNotificationPublicKeyForAccountId:(NSString *)accountId; - (void)setPushNotificationPrivateKey:(NSData *)privateKey forAccountId:(NSString *)accountId; -- (NSData *)pushNotificationPrivateKeyForAccountId:(NSString *)accountId; +- (NSData * _Nullable)pushNotificationPrivateKeyForAccountId:(NSString *)accountId; - (NSString *)pushTokenSHA512; - (void)logCombinedPushToken; - (NSString *)combinedPushToken; diff --git a/NotificationServiceExtension/NotificationService.m b/NotificationServiceExtension/NotificationService.m index 34d0ace60..dc842f171 100644 --- a/NotificationServiceExtension/NotificationService.m +++ b/NotificationServiceExtension/NotificationService.m @@ -85,170 +85,180 @@ - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withConte // Decrypt message NSString *message = [self.bestAttemptContent.userInfo objectForKey:@"subject"]; + NSString *signature = [self.bestAttemptContent.userInfo objectForKey:@"signature"]; + + if (!message || !signature) { + // Without a message or signature there's nothing left to do here + self.contentHandler(self.bestAttemptContent); + return; + } + for (TalkAccount *talkAccount in [TalkAccount allObjects]) { TalkAccount *account = [[TalkAccount alloc] initWithValue:talkAccount]; - NSData *pushNotificationPrivateKey = [[NCKeyChainController sharedInstance] pushNotificationPrivateKeyForAccountId:account.accountId]; - if (message && pushNotificationPrivateKey) { - @try { - NSString *decryptedMessage = [NCPushNotificationsUtils decryptPushNotificationWithMessage:message withDevicePrivateKey:pushNotificationPrivateKey]; - if (decryptedMessage) { - NCPushNotification *pushNotification = [NCPushNotification pushNotificationFromDecryptedString:decryptedMessage withAccountId:account.accountId]; - - if (pushNotification.type == NCPushNotificationTypeAdminNotification) { - // Test notification send through "occ notification:test-push --talk " - // No need to increase the badge or query the server about it - - self.bestAttemptContent.body = pushNotification.subject; - self.contentHandler(self.bestAttemptContent); - return; - } - foundDecryptableMessage = YES; + @try { + NSString *decryptedMessage = [NCPushNotificationsUtils decryptPushNotificationWithMessageBase64:message withSignatureBase64:signature forAccount:account]; - [[RLMRealm defaultRealm] transactionWithBlock:^{ - NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", account.accountId]; - TalkAccount *managedAccount = [TalkAccount objectsWithPredicate:query].firstObject; + if (!decryptedMessage) { + continue; + } - // Update unread notifications counter for push notification account - managedAccount.unreadBadgeNumber += 1; - managedAccount.unreadNotification = (managedAccount.active) ? NO : YES; + NCPushNotification *pushNotification = [NCPushNotification pushNotificationFromDecryptedString:decryptedMessage withAccountId:account.accountId]; - // Make sure we don't accidentally show a notification again, when we check for notifications in the background - if (managedAccount.lastNotificationId < pushNotification.notificationId) { - managedAccount.lastNotificationId = pushNotification.notificationId; - } - }]; + if (pushNotification.type == NCPushNotificationTypeAdminNotification) { + // Test notification send through "occ notification:test-push --talk " + // No need to increase the badge or query the server about it - // Get the total number of unread notifications - NSInteger unreadNotifications = 0; - for (TalkAccount *user in [TalkAccount allObjects]) { - unreadNotifications += user.unreadBadgeNumber; - } + self.bestAttemptContent.body = pushNotification.subject; + self.contentHandler(self.bestAttemptContent); + return; + } + + foundDecryptableMessage = YES; + + [[RLMRealm defaultRealm] transactionWithBlock:^{ + NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", account.accountId]; + TalkAccount *managedAccount = [TalkAccount objectsWithPredicate:query].firstObject; + + // Update unread notifications counter for push notification account + managedAccount.unreadBadgeNumber += 1; + managedAccount.unreadNotification = (managedAccount.active) ? NO : YES; + + // Make sure we don't accidentally show a notification again, when we check for notifications in the background + if (managedAccount.lastNotificationId < pushNotification.notificationId) { + managedAccount.lastNotificationId = pushNotification.notificationId; + } + }]; + + // Get the total number of unread notifications + NSInteger unreadNotifications = 0; + for (TalkAccount *user in [TalkAccount allObjects]) { + unreadNotifications += user.unreadBadgeNumber; + } + + self.bestAttemptContent.body = pushNotification.bodyForRemoteAlerts; + self.bestAttemptContent.threadIdentifier = pushNotification.roomToken; + self.bestAttemptContent.sound = [UNNotificationSound defaultSound]; + self.bestAttemptContent.badge = @(unreadNotifications); - self.bestAttemptContent.body = pushNotification.bodyForRemoteAlerts; - self.bestAttemptContent.threadIdentifier = pushNotification.roomToken; - self.bestAttemptContent.sound = [UNNotificationSound defaultSound]; - self.bestAttemptContent.badge = @(unreadNotifications); + if (pushNotification.type == NCPushNotificationTypeChat) { + // Set category for chat messages to allow interactive notifications + self.bestAttemptContent.categoryIdentifier = @"CATEGORY_CHAT"; + } + + NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init]; + [userInfo setObject:pushNotification.jsonString forKey:@"pushNotification"]; + [userInfo setObject:pushNotification.accountId forKey:@"accountId"]; + [userInfo setObject:@(pushNotification.notificationId) forKey:@"notificationId"]; + self.bestAttemptContent.userInfo = userInfo; + + // Create title and body structure if there is a new line in the subject + NSArray* components = [pushNotification.subject componentsSeparatedByString:@"\n"]; + if (components.count > 1) { + NSString *title = [components objectAtIndex:0]; + NSMutableArray *mutableComponents = [[NSMutableArray alloc] initWithArray:components]; + [mutableComponents removeObjectAtIndex:0]; + NSString *body = [mutableComponents componentsJoinedByString:@"\n"]; + self.bestAttemptContent.title = title; + self.bestAttemptContent.body = body; + } + + // Try to get the notification from the server + NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/apps/notifications/api/v2/notifications/%ld", account.server, (long)pushNotification.notificationId]; + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedCookieStorageForGroupContainerIdentifier:account.accountId]; + configuration.HTTPCookieStorage = cookieStorage; + NCAPISessionManager *apiSessionManager = [[NCAPISessionManager alloc] initWithConfiguration:configuration]; + + NSString *userTokenString = [NSString stringWithFormat:@"%@:%@", account.user, [[NCKeyChainController sharedInstance] tokenForAccountId:account.accountId]]; + NSData *data = [userTokenString dataUsingEncoding:NSUTF8StringEncoding]; + NSString *base64Encoded = [data base64EncodedStringWithOptions:0]; + NSString *authorizationHeader = [[NSString alloc] initWithFormat:@"Basic %@", base64Encoded]; + [apiSessionManager.requestSerializer setValue:authorizationHeader forHTTPHeaderField:@"Authorization"]; + [apiSessionManager.requestSerializer setTimeoutInterval:25]; + + [apiSessionManager GET:URLString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { + NSDictionary *notification = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"]; + NCNotification *serverNotification = [NCNotification notificationWithDictionary:notification]; + + if (!serverNotification) { + self.contentHandler(self.bestAttemptContent); + return; + } + + // Add the serverNotification as userInfo as well -> this can later be used to access the actions directly + [userInfo setObject:notification forKey:@"serverNotification"]; + self.bestAttemptContent.userInfo = userInfo; - if (pushNotification.type == NCPushNotificationTypeChat) { - // Set category for chat messages to allow interactive notifications - self.bestAttemptContent.categoryIdentifier = @"CATEGORY_CHAT"; + if (serverNotification.notificationType == kNCNotificationTypeChat) { + // Only try to adjust the title/body if there are rich parameters + // E.g. for sensitive conversations, there are none, so we use the server provided title/body + if ([serverNotification.subjectRichParameters count] > 0) { + NSAttributedString *attributedMessage = [[NSAttributedString alloc] initWithString:serverNotification.message]; + NSAttributedString *markdownMessage = [SwiftMarkdownObjCBridge parseMarkdownWithMarkdownString:attributedMessage]; + + self.bestAttemptContent.title = serverNotification.chatMessageTitle; + self.bestAttemptContent.body = markdownMessage.string; } - NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init]; - [userInfo setObject:pushNotification.jsonString forKey:@"pushNotification"]; - [userInfo setObject:pushNotification.accountId forKey:@"accountId"]; - [userInfo setObject:@(pushNotification.notificationId) forKey:@"notificationId"]; - self.bestAttemptContent.userInfo = userInfo; - - // Create title and body structure if there is a new line in the subject - NSArray* components = [pushNotification.subject componentsSeparatedByString:@"\n"]; - if (components.count > 1) { - NSString *title = [components objectAtIndex:0]; - NSMutableArray *mutableComponents = [[NSMutableArray alloc] initWithArray:components]; - [mutableComponents removeObjectAtIndex:0]; - NSString *body = [mutableComponents componentsJoinedByString:@"\n"]; - self.bestAttemptContent.title = title; - self.bestAttemptContent.body = body; + NSDictionary *fileDict = [serverNotification.messageRichParameters objectForKey:@"file"]; + if (fileDict && [[fileDict objectForKey:@"preview-available"] boolValue]) { + // First try to create the conversation notification, and only afterwards try to retrieve the image preview + [self createConversationNotificationWithPushNotification:pushNotification withCompletionBlock:^{ + NSString *fileId = [fileDict objectForKey:@"id"]; + NSString *urlString = [NSString stringWithFormat:@"%@/index.php/core/preview?fileId=%@&x=-1&y=%ld&a=1&forceIcon=1", account.server, fileId, 512L]; + + NSURL *url = [NSURL URLWithString:urlString]; + + NSString *userAgent = [NSString stringWithFormat:@"Mozilla/5.0 (iOS) Nextcloud-Talk v%@", + [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]]; + + [[SDWebImageDownloader sharedDownloader] setValue:userAgent forHTTPHeaderField:@"User-Agent"]; + [SDWebImageDownloader sharedDownloader].config.downloadTimeout = 25.0; + + SDWebImageOptions options = SDWebImageRetryFailed | SDWebImageRefreshCached; + + NSDictionary *headerDictionary = @{@"Authorization" : authorizationHeader}; + SDWebImageDownloaderRequestModifier *requestModifier = [[SDWebImageDownloaderRequestModifier alloc] initWithHeaders:headerDictionary]; + + [[SDWebImageManager sharedManager] loadImageWithURL:url options:options context:@{SDWebImageContextDownloadRequestModifier : requestModifier} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) { + if (error == nil && image) { + UNNotificationAttachment *attachment = [self getNotificationAttachmentFromImage:image forAccountId:account.accountId]; + + if (attachment) { + self.bestAttemptContent.attachments = @[attachment]; + } + } + + [self showBestAttemptNotification]; + }]; + }]; + + // Stop here because the downloader completion blocks will take care of creating the conversation notification + return; } - // Try to get the notification from the server - NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/apps/notifications/api/v2/notifications/%ld", account.server, (long)pushNotification.notificationId]; - NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; - NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedCookieStorageForGroupContainerIdentifier:account.accountId]; - configuration.HTTPCookieStorage = cookieStorage; - NCAPISessionManager *apiSessionManager = [[NCAPISessionManager alloc] initWithConfiguration:configuration]; - - NSString *userTokenString = [NSString stringWithFormat:@"%@:%@", account.user, [[NCKeyChainController sharedInstance] tokenForAccountId:account.accountId]]; - NSData *data = [userTokenString dataUsingEncoding:NSUTF8StringEncoding]; - NSString *base64Encoded = [data base64EncodedStringWithOptions:0]; - NSString *authorizationHeader = [[NSString alloc] initWithFormat:@"Basic %@", base64Encoded]; - [apiSessionManager.requestSerializer setValue:authorizationHeader forHTTPHeaderField:@"Authorization"]; - [apiSessionManager.requestSerializer setTimeoutInterval:25]; - - [apiSessionManager GET:URLString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { - NSDictionary *notification = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"]; - NCNotification *serverNotification = [NCNotification notificationWithDictionary:notification]; - - if (!serverNotification) { - self.contentHandler(self.bestAttemptContent); - return; - } - - // Add the serverNotification as userInfo as well -> this can later be used to access the actions directly - [userInfo setObject:notification forKey:@"serverNotification"]; - self.bestAttemptContent.userInfo = userInfo; - - if (serverNotification.notificationType == kNCNotificationTypeChat) { - // Only try to adjust the title/body if there are rich parameters - // E.g. for sensitive conversations, there are none, so we use the server provided title/body - if ([serverNotification.subjectRichParameters count] > 0) { - NSAttributedString *attributedMessage = [[NSAttributedString alloc] initWithString:serverNotification.message]; - NSAttributedString *markdownMessage = [SwiftMarkdownObjCBridge parseMarkdownWithMarkdownString:attributedMessage]; - - self.bestAttemptContent.title = serverNotification.chatMessageTitle; - self.bestAttemptContent.body = markdownMessage.string; - } - - NSDictionary *fileDict = [serverNotification.messageRichParameters objectForKey:@"file"]; - if (fileDict && [[fileDict objectForKey:@"preview-available"] boolValue]) { - // First try to create the conversation notification, and only afterwards try to retrieve the image preview - [self createConversationNotificationWithPushNotification:pushNotification withCompletionBlock:^{ - NSString *fileId = [fileDict objectForKey:@"id"]; - NSString *urlString = [NSString stringWithFormat:@"%@/index.php/core/preview?fileId=%@&x=-1&y=%ld&a=1&forceIcon=1", account.server, fileId, 512L]; - - NSURL *url = [NSURL URLWithString:urlString]; - - NSString *userAgent = [NSString stringWithFormat:@"Mozilla/5.0 (iOS) Nextcloud-Talk v%@", - [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]]; - - [[SDWebImageDownloader sharedDownloader] setValue:userAgent forHTTPHeaderField:@"User-Agent"]; - [SDWebImageDownloader sharedDownloader].config.downloadTimeout = 25.0; - - SDWebImageOptions options = SDWebImageRetryFailed | SDWebImageRefreshCached; - - NSDictionary *headerDictionary = @{@"Authorization" : authorizationHeader}; - SDWebImageDownloaderRequestModifier *requestModifier = [[SDWebImageDownloaderRequestModifier alloc] initWithHeaders:headerDictionary]; - - [[SDWebImageManager sharedManager] loadImageWithURL:url options:options context:@{SDWebImageContextDownloadRequestModifier : requestModifier} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) { - if (error == nil && image) { - UNNotificationAttachment *attachment = [self getNotificationAttachmentFromImage:image forAccountId:account.accountId]; - - if (attachment) { - self.bestAttemptContent.attachments = @[attachment]; - } - } - - [self showBestAttemptNotification]; - }]; - }]; - - // Stop here because the downloader completion blocks will take care of creating the conversation notification - return; - } - - } else if (serverNotification.notificationType == kNCNotificationTypeRecording) { - self.bestAttemptContent.categoryIdentifier = @"CATEGORY_RECORDING"; - self.bestAttemptContent.title = serverNotification.subject; - self.bestAttemptContent.body = serverNotification.message; - } else if (serverNotification.notificationType == kNCNotificationTypeFederation) { - self.bestAttemptContent.categoryIdentifier = @"CATEGORY_FEDERATION"; - self.bestAttemptContent.title = serverNotification.subject; - self.bestAttemptContent.body = serverNotification.message; - - [[NCDatabaseManager sharedInstance] increasePendingFederationInvitationForAccountId:account.accountId]; - } - - [self createConversationNotificationWithPushNotificationAndShow:pushNotification]; - } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { - // Even if the server request fails, we should try to create a conversation notifications - [self createConversationNotificationWithPushNotificationAndShow:pushNotification]; - }]; + } else if (serverNotification.notificationType == kNCNotificationTypeRecording) { + self.bestAttemptContent.categoryIdentifier = @"CATEGORY_RECORDING"; + self.bestAttemptContent.title = serverNotification.subject; + self.bestAttemptContent.body = serverNotification.message; + } else if (serverNotification.notificationType == kNCNotificationTypeFederation) { + self.bestAttemptContent.categoryIdentifier = @"CATEGORY_FEDERATION"; + self.bestAttemptContent.title = serverNotification.subject; + self.bestAttemptContent.body = serverNotification.message; + + [[NCDatabaseManager sharedInstance] increasePendingFederationInvitationForAccountId:account.accountId]; } - } @catch (NSException *exception) { - NSLog(@"An error ocurred decrypting the message. %@", exception); - continue; - } + + [self createConversationNotificationWithPushNotificationAndShow:pushNotification]; + } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { + // Even if the server request fails, we should try to create a conversation notifications + [self createConversationNotificationWithPushNotificationAndShow:pushNotification]; + }]; + + } @catch (NSException *exception) { + NSLog(@"An error ocurred decrypting the message. %@", exception); + continue; } }