diff --git a/Podfile b/Podfile index 9ebc39636..5efd26a36 100644 --- a/Podfile +++ b/Podfile @@ -6,7 +6,6 @@ inhibit_all_warnings! # Dependencies to be included in the app and all extensions/frameworks abstract_target 'GlobalDependencies' do - pod 'PromiseKit' pod 'CryptoSwift' # FIXME: If https://github.com/jedisct1/swift-sodium/pull/249 gets resolved then revert this back to the standard pod pod 'Sodium', :git => 'https://github.com/oxen-io/session-ios-swift-sodium.git', branch: 'session-build' diff --git a/Podfile.lock b/Podfile.lock index 603dc6ae7..eca83a9d1 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -28,15 +28,6 @@ PODS: - NVActivityIndicatorView/Base (= 5.1.1) - NVActivityIndicatorView/Base (5.1.1) - OpenSSL-Universal (1.1.1300) - - PromiseKit (6.18.1): - - PromiseKit/CorePromise (= 6.18.1) - - PromiseKit/Foundation (= 6.18.1) - - PromiseKit/UIKit (= 6.18.1) - - PromiseKit/CorePromise (6.18.1) - - PromiseKit/Foundation (6.18.1): - - PromiseKit/CorePromise - - PromiseKit/UIKit (6.18.1): - - PromiseKit/CorePromise - PureLayout (3.1.9) - Quick (5.0.1) - Reachability (3.2) @@ -130,7 +121,6 @@ DEPENDENCIES: - GRDB.swift/SQLCipher - Nimble - NVActivityIndicatorView - - PromiseKit - PureLayout (~> 3.1.8) - Quick - Reachability @@ -155,7 +145,6 @@ SPEC REPOS: - Nimble - NVActivityIndicatorView - OpenSSL-Universal - - PromiseKit - PureLayout - Quick - Reachability @@ -209,7 +198,6 @@ SPEC CHECKSUMS: Nimble: 5316ef81a170ce87baf72dd961f22f89a602ff84 NVActivityIndicatorView: 1f6c5687f1171810aa27a3296814dc2d7dec3667 OpenSSL-Universal: e7311447fd2419f57420c79524b641537387eff2 - PromiseKit: 49d70c53d5d20e346beaea4b276b5dd2ab446bb4 PureLayout: 5fb5e5429519627d60d079ccb1eaa7265ce7cf88 Quick: 749aa754fd1e7d984f2000fe051e18a3a9809179 Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 @@ -224,6 +212,6 @@ SPEC CHECKSUMS: YYImage: f1ddd15ac032a58b78bbed1e012b50302d318331 ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: ce9209e5c7ea252b767ee08075d4e4f8b49e487b +PODFILE CHECKSUM: 6ee08fc446436a2534ec34c041c3b9bc39801870 COCOAPODS: 1.11.3 diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index a1d6257db..638c753ea 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -278,7 +278,6 @@ C32C598A256D0664003C73A2 /* SNProtoEnvelope+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */; }; C32C599E256DB02B003C73A2 /* TypingIndicators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA87255A57FC00E217F9 /* TypingIndicators.swift */; }; C32C5A24256DB7DB003C73A2 /* SNUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB6B255A580F00E217F9 /* SNUserDefaults.swift */; }; - C32C5A47256DB8F0003C73A2 /* ECKeyPair+Hexadecimal.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA73255A57FA00E217F9 /* ECKeyPair+Hexadecimal.swift */; }; C32C5A48256DB8F0003C73A2 /* BuildConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAA8255A57FF00E217F9 /* BuildConfiguration.swift */; }; C32C5A88256DBCF9003C73A2 /* MessageReceiver+ClosedGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = C32C5A87256DBCF9003C73A2 /* MessageReceiver+ClosedGroups.swift */; }; C32C5B48256DC211003C73A2 /* NSNotificationCenter+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB6C255A580F00E217F9 /* NSNotificationCenter+OWS.m */; }; @@ -344,7 +343,6 @@ C33FDDD0255A582000E217F9 /* FunctionalUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDC16255A581E00E217F9 /* FunctionalUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; C3402FE52559036600EA6424 /* SessionUIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C331FF1B2558F9D300070591 /* SessionUIKit.framework */; }; C3471ECB2555356A00297E91 /* MessageSender+Encryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3471ECA2555356A00297E91 /* MessageSender+Encryption.swift */; }; - C3471ED42555386B00297E91 /* AESGCM.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D72553860B00C340D1 /* AESGCM.swift */; }; C3471F4C25553AB000297E91 /* MessageReceiver+Decryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3471F4B25553AB000297E91 /* MessageReceiver+Decryption.swift */; }; C34C8F7423A7830B00D82669 /* SpaceMono-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C34C8F7323A7830A00D82669 /* SpaceMono-Bold.ttf */; }; C352A2FF25574B6300338F3E /* MessageSendJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352A2FE25574B6300338F3E /* MessageSendJob.swift */; }; @@ -427,7 +425,6 @@ C3A71D1E25589AC30043A11F /* WebSocketProto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D1C25589AC30043A11F /* WebSocketProto.swift */; }; C3A71D1F25589AC30043A11F /* WebSocketResources.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D1D25589AC30043A11F /* WebSocketResources.pb.swift */; }; C3A71F892558BA9F0043A11F /* Mnemonic.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71F882558BA9F0043A11F /* Mnemonic.swift */; }; - C3A7211A2558BCA10043A11F /* DiffieHellman.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D662558A0170043A11F /* DiffieHellman.swift */; }; C3AAFFF225AE99710089E6DD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AAFFF125AE99710089E6DD /* AppDelegate.swift */; }; C3ADC66126426688005F1414 /* ShareNavController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3ADC66026426688005F1414 /* ShareNavController.swift */; }; C3BBE0A82554D4DE0050F1E3 /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D92553860B00C340D1 /* JSON.swift */; }; @@ -655,7 +652,7 @@ FD705A92278D051200F16121 /* ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A91278D051200F16121 /* ReusableView.swift */; }; FD7115EB28C5D78E00B47552 /* ThreadSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD7115EA28C5D78E00B47552 /* ThreadSettingsViewModel.swift */; }; FD7115F228C6CB3900B47552 /* _010_AddThreadIdToFTS.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD7115F128C6CB3900B47552 /* _010_AddThreadIdToFTS.swift */; }; - FD7115F428C71EB200B47552 /* ThreadDisappearingMessagesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD7115F328C71EB200B47552 /* ThreadDisappearingMessagesViewModel.swift */; }; + FD7115F428C71EB200B47552 /* ThreadDisappearingMessagesSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD7115F328C71EB200B47552 /* ThreadDisappearingMessagesSettingsViewModel.swift */; }; FD7115F828C8151C00B47552 /* DisposableBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD7115F728C8151C00B47552 /* DisposableBarButtonItem.swift */; }; FD7115FA28C8153400B47552 /* UIBarButtonItem+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD7115F928C8153400B47552 /* UIBarButtonItem+Combine.swift */; }; FD7115FC28C8155800B47552 /* Publisher+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD7115FB28C8155800B47552 /* Publisher+Utilities.swift */; }; @@ -801,6 +798,8 @@ FDD2506E283711D600198BDA /* DifferenceKit+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDD2506D283711D600198BDA /* DifferenceKit+Utilities.swift */; }; FDD250702837199200198BDA /* GarbageCollectionJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDD2506F2837199200198BDA /* GarbageCollectionJob.swift */; }; FDD250722837234B00198BDA /* MediaGalleryNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDD250712837234B00198BDA /* MediaGalleryNavigationController.swift */; }; + FDE658A129418C7900A33BC1 /* CryptoKit+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE658A029418C7900A33BC1 /* CryptoKit+Utilities.swift */; }; + FDE658A329418E2F00A33BC1 /* KeyPair.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE658A229418E2F00A33BC1 /* KeyPair.swift */; }; FDE77F6B280FEB28002CFC5D /* ControlMessageProcessRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE77F6A280FEB28002CFC5D /* ControlMessageProcessRecord.swift */; }; FDED2E3C282E1B5D00B2CD2A /* UICollectionView+ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDED2E3B282E1B5D00B2CD2A /* UICollectionView+ReusableView.swift */; }; FDF0B73C27FFD3D6004C14C5 /* LinkPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B73B27FFD3D6004C14C5 /* LinkPreview.swift */; }; @@ -1405,7 +1404,6 @@ C33FD9AD255A548A00E217F9 /* SignalUtilitiesKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SignalUtilitiesKit.h; sourceTree = ""; }; C33FD9AE255A548A00E217F9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C33FDA6F255A57FA00E217F9 /* ReachabilityManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityManager.swift; sourceTree = ""; }; - C33FDA73255A57FA00E217F9 /* ECKeyPair+Hexadecimal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ECKeyPair+Hexadecimal.swift"; sourceTree = ""; }; C33FDA7A255A57FB00E217F9 /* NSRegularExpression+SSK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSRegularExpression+SSK.swift"; sourceTree = ""; }; C33FDA87255A57FC00E217F9 /* TypingIndicators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypingIndicators.swift; sourceTree = ""; }; C33FDA8B255A57FD00E217F9 /* AppVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppVersion.m; sourceTree = ""; }; @@ -1573,7 +1571,6 @@ C3A71D0A2558989C0043A11F /* MessageWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageWrapper.swift; sourceTree = ""; }; C3A71D1C25589AC30043A11F /* WebSocketProto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebSocketProto.swift; sourceTree = ""; }; C3A71D1D25589AC30043A11F /* WebSocketResources.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebSocketResources.pb.swift; sourceTree = ""; }; - C3A71D662558A0170043A11F /* DiffieHellman.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiffieHellman.swift; sourceTree = ""; }; C3A71F882558BA9F0043A11F /* Mnemonic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mnemonic.swift; sourceTree = ""; }; C3A8AF752665B03900A467FE /* hi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hi; path = hi.lproj/Localizable.strings; sourceTree = ""; }; C3A8AF762665F97A00A467FE /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; @@ -1592,7 +1589,6 @@ C3C2A5D22553860900C340D1 /* String+Trimming.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Trimming.swift"; sourceTree = ""; }; C3C2A5D42553860A00C340D1 /* Threading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Threading.swift; sourceTree = ""; }; C3C2A5D52553860A00C340D1 /* Dictionary+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+Utilities.swift"; sourceTree = ""; }; - C3C2A5D72553860B00C340D1 /* AESGCM.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AESGCM.swift; sourceTree = ""; }; C3C2A5D82553860B00C340D1 /* Data+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Utilities.swift"; sourceTree = ""; }; C3C2A5D92553860B00C340D1 /* JSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSON.swift; sourceTree = ""; }; C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SessionUtilitiesKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1773,7 +1769,7 @@ FD7115EA28C5D78E00B47552 /* ThreadSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadSettingsViewModel.swift; sourceTree = ""; }; FD7115EF28C5D7DE00B47552 /* SessionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionHeaderView.swift; sourceTree = ""; }; FD7115F128C6CB3900B47552 /* _010_AddThreadIdToFTS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _010_AddThreadIdToFTS.swift; sourceTree = ""; }; - FD7115F328C71EB200B47552 /* ThreadDisappearingMessagesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadDisappearingMessagesViewModel.swift; sourceTree = ""; }; + FD7115F328C71EB200B47552 /* ThreadDisappearingMessagesSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadDisappearingMessagesSettingsViewModel.swift; sourceTree = ""; }; FD7115F728C8151C00B47552 /* DisposableBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisposableBarButtonItem.swift; sourceTree = ""; }; FD7115F928C8153400B47552 /* UIBarButtonItem+Combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIBarButtonItem+Combine.swift"; sourceTree = ""; }; FD7115FB28C8155800B47552 /* Publisher+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Publisher+Utilities.swift"; sourceTree = ""; }; @@ -1910,6 +1906,8 @@ FDD2506D283711D600198BDA /* DifferenceKit+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DifferenceKit+Utilities.swift"; sourceTree = ""; }; FDD2506F2837199200198BDA /* GarbageCollectionJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GarbageCollectionJob.swift; sourceTree = ""; }; FDD250712837234B00198BDA /* MediaGalleryNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaGalleryNavigationController.swift; sourceTree = ""; }; + FDE658A029418C7900A33BC1 /* CryptoKit+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CryptoKit+Utilities.swift"; sourceTree = ""; }; + FDE658A229418E2F00A33BC1 /* KeyPair.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyPair.swift; sourceTree = ""; }; FDE7214F287E50D50093DF33 /* ProtoWrappers.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = ProtoWrappers.py; sourceTree = ""; }; FDE72150287E50D50093DF33 /* LintLocalizableStrings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LintLocalizableStrings.swift; sourceTree = ""; }; FDE77F68280F9EDA002CFC5D /* JobRunnerError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JobRunnerError.swift; sourceTree = ""; }; @@ -2506,11 +2504,10 @@ B8A582AC258C653C00AFD84C /* Crypto */ = { isa = PBXGroup; children = ( - C3C2A5D72553860B00C340D1 /* AESGCM.swift */, + FDE658A029418C7900A33BC1 /* CryptoKit+Utilities.swift */, C3C2ABD12553C6C900C340D1 /* Data+SecureRandom.swift */, - C3A71D662558A0170043A11F /* DiffieHellman.swift */, - C33FDA73255A57FA00E217F9 /* ECKeyPair+Hexadecimal.swift */, B88FA7FA26114EA70049422F /* Hex.swift */, + FDE658A229418E2F00A33BC1 /* KeyPair.swift */, C3A71F882558BA9F0043A11F /* Mnemonic.swift */, ); path = Crypto; @@ -2741,7 +2738,7 @@ 3427C64120F500DE00EEC730 /* OWSMessageTimerView.h */, 3427C64220F500DF00EEC730 /* OWSMessageTimerView.m */, FD7115EA28C5D78E00B47552 /* ThreadSettingsViewModel.swift */, - FD7115F328C71EB200B47552 /* ThreadDisappearingMessagesViewModel.swift */, + FD7115F328C71EB200B47552 /* ThreadDisappearingMessagesSettingsViewModel.swift */, ); path = Settings; sourceTree = ""; @@ -5457,8 +5454,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FDE658A129418C7900A33BC1 /* CryptoKit+Utilities.swift in Sources */, FDFD645927F26C6800808CA1 /* Array+Utilities.swift in Sources */, - C32C5A47256DB8F0003C73A2 /* ECKeyPair+Hexadecimal.swift in Sources */, 7B1D74B027C365960030B423 /* Timer+MainThread.swift in Sources */, C32C5D83256DD5B6003C73A2 /* SSKKeychainStorage.swift in Sources */, FDF8488329405A12007DCAE5 /* BatchResponse.swift in Sources */, @@ -5486,13 +5483,13 @@ FD71160228C8255900B47552 /* UIControl+Combine.swift in Sources */, FD9004152818B46300ABAAF6 /* JobRunner.swift in Sources */, FDF8487929405906007DCAE5 /* HTTPQueryParam.swift in Sources */, - C3A7211A2558BCA10043A11F /* DiffieHellman.swift in Sources */, FD17D7CA27F546D900122BE0 /* _001_InitialSetupMigration.swift in Sources */, C3D9E4D12567777D0040E4F3 /* OWSMediaUtils.swift in Sources */, C3BBE0AA2554D4DE0050F1E3 /* Dictionary+Utilities.swift in Sources */, FD37EA1128AB34B3003AE748 /* TypedTableAlteration.swift in Sources */, C3D9E4DA256778410040E4F3 /* UIImage+OWS.m in Sources */, C32C600F256E07F5003C73A2 /* NSUserDefaults+OWS.m in Sources */, + FDE658A329418E2F00A33BC1 /* KeyPair.swift in Sources */, FD37E9FF28A5F2CD003AE748 /* Configuration.swift in Sources */, FDB7400B28EB99A70094D718 /* TimeInterval+Utilities.swift in Sources */, C3D9E35E25675F640040E4F3 /* OWSFileSystem.m in Sources */, @@ -5513,7 +5510,6 @@ FD77289E284EF1C50018502F /* Sodium+Utilities.swift in Sources */, B8856DE6256F15F2001CE70E /* String+SSK.swift in Sources */, FDF2220F281B55E6000A4995 /* QueryInterfaceRequest+Utilities.swift in Sources */, - C3471ED42555386B00297E91 /* AESGCM.swift in Sources */, FDF848EF294067E4007DCAE5 /* URLResponse+Utilities.swift in Sources */, FD848B9A28442CE6000E298B /* StorageError.swift in Sources */, FD17D7B827F51ECA00122BE0 /* Migration.swift in Sources */, @@ -5875,7 +5871,7 @@ 45B5360E206DD8BB00D61655 /* UIResponder+OWS.swift in Sources */, 7B9F71C928470667006DFE7B /* ReactionListSheet.swift in Sources */, 7B7037452834BCC0000DCF35 /* ReactionView.swift in Sources */, - FD7115F428C71EB200B47552 /* ThreadDisappearingMessagesViewModel.swift in Sources */, + FD7115F428C71EB200B47552 /* ThreadDisappearingMessagesSettingsViewModel.swift in Sources */, B8D84ECF25E3108A005A043E /* ExpandingAttachmentsButton.swift in Sources */, 7B7CB190270FB2150079FF93 /* MiniCallView.swift in Sources */, 7B13E1E92810F01300BD4F64 /* SessionCallManager+Action.swift in Sources */, diff --git a/Session/Conversations/Settings/ThreadDisappearingMessagesViewModel.swift b/Session/Conversations/Settings/ThreadDisappearingMessagesSettingsViewModel.swift similarity index 96% rename from Session/Conversations/Settings/ThreadDisappearingMessagesViewModel.swift rename to Session/Conversations/Settings/ThreadDisappearingMessagesSettingsViewModel.swift index 8cdfdce1a..ae636edb5 100644 --- a/Session/Conversations/Settings/ThreadDisappearingMessagesViewModel.swift +++ b/Session/Conversations/Settings/ThreadDisappearingMessagesSettingsViewModel.swift @@ -8,7 +8,7 @@ import SessionUIKit import SessionMessagingKit import SessionUtilitiesKit -class ThreadDisappearingMessagesViewModel: SessionTableViewModel { +class ThreadDisappearingMessagesSettingsViewModel: SessionTableViewModel { // MARK: - Config enum NavButton: Equatable { diff --git a/Session/Conversations/Settings/ThreadSettingsViewModel.swift b/Session/Conversations/Settings/ThreadSettingsViewModel.swift index aa0e42944..2f225fabe 100644 --- a/Session/Conversations/Settings/ThreadSettingsViewModel.swift +++ b/Session/Conversations/Settings/ThreadSettingsViewModel.swift @@ -83,8 +83,19 @@ class ThreadSettingsViewModel: SessionTableViewModel = { - isEditing - .map { isEditing in (isEditing ? .editing : .standard) } + Publishers + .CombineLatest( + isEditing, + textChanged + .handleEvents( + receiveOutput: { [weak self] value, _ in + self?.editedDisplayName = value + } + ) + .filter { _ in false } + .prepend((nil, .nickname)) + ) + .map { isEditing, _ -> NavState in (isEditing ? .editing : .standard) } .removeDuplicates() .prepend(.standard) // Initial value .shareReplay(1) @@ -429,7 +440,7 @@ class ThreadSettingsViewModel: SessionTableViewModel = { Publishers .CombineLatest( - isEditing - .map { isEditing in isEditing }, + isEditing, textChanged .handleEvents( receiveOutput: { [weak self] value, _ in @@ -111,6 +110,7 @@ class SettingsViewModel: SessionTableViewModel NavState in (isEditing ? .editing : .standard) } .removeDuplicates() .prepend(.standard) // Initial value + .shareReplay(1) .eraseToAnyPublisher() }() diff --git a/SessionMessagingKit/Database/Migrations/_002_SetupStandardJobs.swift b/SessionMessagingKit/Database/Migrations/_002_SetupStandardJobs.swift index dfc99d450..8c869e44d 100644 --- a/SessionMessagingKit/Database/Migrations/_002_SetupStandardJobs.swift +++ b/SessionMessagingKit/Database/Migrations/_002_SetupStandardJobs.swift @@ -2,7 +2,6 @@ import Foundation import GRDB -import Curve25519Kit import SessionUtilitiesKit import SessionSnodeKit diff --git a/SessionMessagingKit/Database/Migrations/_003_YDBToGRDBMigration.swift b/SessionMessagingKit/Database/Migrations/_003_YDBToGRDBMigration.swift index b428c4713..d0aedf700 100644 --- a/SessionMessagingKit/Database/Migrations/_003_YDBToGRDBMigration.swift +++ b/SessionMessagingKit/Database/Migrations/_003_YDBToGRDBMigration.swift @@ -4,7 +4,6 @@ import Foundation import AVKit import GRDB import YapDatabase -import Curve25519Kit import SessionUtilitiesKit import SessionSnodeKit diff --git a/SessionMessagingKit/Database/Migrations/_004_RemoveLegacyYDB.swift b/SessionMessagingKit/Database/Migrations/_004_RemoveLegacyYDB.swift index 97aa7462e..f8943a967 100644 --- a/SessionMessagingKit/Database/Migrations/_004_RemoveLegacyYDB.swift +++ b/SessionMessagingKit/Database/Migrations/_004_RemoveLegacyYDB.swift @@ -2,7 +2,6 @@ import Foundation import GRDB -import Curve25519Kit import SessionUtilitiesKit import SessionSnodeKit diff --git a/SessionMessagingKit/Database/Models/DisappearingMessageConfiguration.swift b/SessionMessagingKit/Database/Models/DisappearingMessageConfiguration.swift index fbbf24242..e46e06cbb 100644 --- a/SessionMessagingKit/Database/Models/DisappearingMessageConfiguration.swift +++ b/SessionMessagingKit/Database/Models/DisappearingMessageConfiguration.swift @@ -125,100 +125,3 @@ extension DisappearingMessagesConfiguration { return (validDurationsSeconds.max() ?? 0) }() } - -// MARK: - Objective-C Support - -// TODO: Remove this when possible - -@objc(SMKDisappearingMessagesConfiguration) -public class SMKDisappearingMessagesConfiguration: NSObject { - @objc public static var maxDurationSeconds: UInt = UInt(DisappearingMessagesConfiguration.maxDurationSeconds) - - @objc public static var validDurationsSeconds: [UInt] = DisappearingMessagesConfiguration - .validDurationsSeconds - .map { UInt($0) } - - @objc(isEnabledFor:) - public static func isEnabled(for threadId: String) -> Bool { - return Storage.shared - .read { db in - try DisappearingMessagesConfiguration - .select(.isEnabled) - .filter(id: threadId) - .asRequest(of: Bool.self) - .fetchOne(db) - } - .defaulting(to: false) - } - - @objc(durationIndexFor:) - public static func durationIndex(for threadId: String) -> Int { - let durationSeconds: TimeInterval = Storage.shared - .read { db in - try DisappearingMessagesConfiguration - .select(.durationSeconds) - .filter(id: threadId) - .asRequest(of: TimeInterval.self) - .fetchOne(db) - } - .defaulting(to: DisappearingMessagesConfiguration.defaultDuration) - - return DisappearingMessagesConfiguration.validDurationsSeconds - .firstIndex(of: durationSeconds) - .defaulting(to: 0) - } - - @objc(durationStringFor:) - public static func durationString(for index: Int) -> String { - let durationSeconds: TimeInterval = ( - index >= 0 && index < DisappearingMessagesConfiguration.validDurationsSeconds.count ? - DisappearingMessagesConfiguration.validDurationsSeconds[index] : - DisappearingMessagesConfiguration.validDurationsSeconds[0] - ) - - return floor(durationSeconds).formatted(format: .long) - } - - @objc(update:isEnabled:durationIndex:) - public static func update(_ threadId: String, isEnabled: Bool, durationIndex: Int) { - let durationSeconds: TimeInterval = ( - durationIndex >= 0 && durationIndex < DisappearingMessagesConfiguration.validDurationsSeconds.count ? - DisappearingMessagesConfiguration.validDurationsSeconds[durationIndex] : - DisappearingMessagesConfiguration.validDurationsSeconds[0] - ) - - Storage.shared.write { db in - guard let thread: SessionThread = try SessionThread.fetchOne(db, id: threadId) else { - return - } - - let config: DisappearingMessagesConfiguration = try DisappearingMessagesConfiguration - .fetchOne(db, id: threadId) - .defaulting(to: DisappearingMessagesConfiguration.defaultWith(threadId)) - .with( - isEnabled: isEnabled, - durationSeconds: durationSeconds - ) - .saved(db) - - let interaction: Interaction = try Interaction( - threadId: threadId, - authorId: getUserHexEncodedPublicKey(db), - variant: .infoDisappearingMessagesUpdate, - body: config.messageInfoString(with: nil), - timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)) - ) - .inserted(db) - - try MessageSender.send( - db, - message: ExpirationTimerUpdate( - syncTarget: nil, - duration: UInt32(floor(isEnabled ? durationSeconds : 0)) - ), - interactionId: interaction.id, - in: thread - ) - } - } -} diff --git a/SessionMessagingKit/Database/Models/GroupMember.swift b/SessionMessagingKit/Database/Models/GroupMember.swift index 4cfe0abd4..2ccd56d68 100644 --- a/SessionMessagingKit/Database/Models/GroupMember.swift +++ b/SessionMessagingKit/Database/Models/GroupMember.swift @@ -60,39 +60,3 @@ public struct GroupMember: Codable, Equatable, FetchableRecord, PersistableRecor self.isHidden = isHidden } } - -// MARK: - Objective-C Support - -// FIXME: Remove when possible - -@objc(SMKGroupMember) -public class SMKGroupMember: NSObject { - @objc(isCurrentUserMemberOf:) - public static func isCurrentUserMember(of groupId: String) -> Bool { - return Storage.shared.read { db in - let userPublicKey: String = getUserHexEncodedPublicKey(db) - let numEntries: Int = try GroupMember - .filter(GroupMember.Columns.groupId == groupId) - .filter(GroupMember.Columns.profileId == userPublicKey) - .fetchCount(db) - - return (numEntries > 0) - } - .defaulting(to: false) - } - - @objc(isCurrentUserAdminOf:) - public static func isCurrentUserAdmin(of groupId: String) -> Bool { - return Storage.shared.read { db in - let userPublicKey: String = getUserHexEncodedPublicKey(db) - let numEntries: Int = try GroupMember - .filter(GroupMember.Columns.groupId == groupId) - .filter(GroupMember.Columns.profileId == userPublicKey) - .filter(GroupMember.Columns.role == GroupMember.Role.admin) - .fetchCount(db) - - return (numEntries > 0) - } - .defaulting(to: false) - } -} diff --git a/SessionMessagingKit/Database/Models/Profile.swift b/SessionMessagingKit/Database/Models/Profile.swift index f2e6c5237..a37a7bb2f 100644 --- a/SessionMessagingKit/Database/Models/Profile.swift +++ b/SessionMessagingKit/Database/Models/Profile.swift @@ -351,33 +351,3 @@ public extension Profile { } } } - -// MARK: - Objective-C Support - -// FIXME: Remove when possible - -@objc(SMKProfile) -public class SMKProfile: NSObject { - @objc public static func displayName(id: String) -> String { - return Profile.displayName(id: id) - } - - @objc public static func displayName(id: String, customFallback: String) -> String { - return Profile.displayName(id: id, customFallback: customFallback) - } - - @objc(displayNameAfterSavingNickname:forProfileId:) - public static func displayNameAfterSaving(nickname: String?, for profileId: String) -> String { - return Storage.shared.write { db in - let profile: Profile = Profile.fetchOrCreate(id: profileId) - let targetNickname: String? = ((nickname ?? "").count > 0 ? nickname : nil) - - try Profile - .filter(id: profile.id) - .updateAll(db, Profile.Columns.nickname.set(to: targetNickname)) - - return (targetNickname ?? profile.name) - } - .defaulting(to: "") - } -} diff --git a/SessionMessagingKit/Open Groups/OpenGroupAPI.swift b/SessionMessagingKit/Open Groups/OpenGroupAPI.swift index f485c57bb..688ec61fb 100644 --- a/SessionMessagingKit/Open Groups/OpenGroupAPI.swift +++ b/SessionMessagingKit/Open Groups/OpenGroupAPI.swift @@ -4,7 +4,6 @@ import Foundation import Combine import GRDB import Sodium -import Curve25519Kit import SessionSnodeKit import SessionUtilitiesKit @@ -1300,24 +1299,24 @@ public enum OpenGroupAPI { guard let blindedKeyPair: Box.KeyPair = dependencies.sodium.blindedKeyPair(serverPublicKey: serverPublicKey, edKeyPair: userEdKeyPair, genericHash: dependencies.genericHash) else { return nil } - + guard let signatureResult: Bytes = dependencies.sodium.sogsSignature(message: messageBytes, secretKey: userEdKeyPair.secretKey, blindedSecretKey: blindedKeyPair.secretKey, blindedPublicKey: blindedKeyPair.publicKey) else { return nil } - + return ( publicKey: SessionId(.blinded, publicKey: blindedKeyPair.publicKey).hexString, signature: signatureResult ) } - + // Otherwise sign using the fallback type switch signingType { case .unblinded: guard let signatureResult: Bytes = dependencies.sign.signature(message: messageBytes, secretKey: userEdKeyPair.secretKey) else { return nil } - + return ( publicKey: SessionId(.unblinded, publicKey: userEdKeyPair.publicKey).hexString, signature: signatureResult diff --git a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageSender+ClosedGroups.swift b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageSender+ClosedGroups.swift index ba6b1903c..e3fa2ec73 100644 --- a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageSender+ClosedGroups.swift +++ b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageSender+ClosedGroups.swift @@ -19,9 +19,13 @@ extension MessageSender { var members: Set = members // Generate the group's public key - let groupPublicKey = Curve25519.generateKeyPair().hexEncodedPublicKey // Includes the 'SessionId.Prefix.standard' prefix + let groupKeyPair: ECKeyPair = Curve25519.generateKeyPair() + let groupPublicKey: String = KeyPair( + publicKey: groupKeyPair.publicKey.bytes, + secretKey: groupKeyPair.privateKey.bytes + ).hexEncodedPublicKey // Includes the 'SessionId.Prefix.standard' prefix // Generate the key pair that'll be used for encryption and decryption - let encryptionKeyPair = Curve25519.generateKeyPair() + let encryptionKeyPair: ECKeyPair = Curve25519.generateKeyPair() // Create the group members.insert(userPublicKey) // Ensure the current user is included in the member list diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift index e1ac4b392..0f2f7b2d5 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift @@ -3,8 +3,6 @@ import Foundation import GRDB import Sodium -import CryptoSwift -import Curve25519Kit import SessionUtilitiesKit extension MessageReceiver { diff --git a/SessionMessagingKitTests/_TestUtilities/TestOnionRequestAPI.swift b/SessionMessagingKitTests/_TestUtilities/TestOnionRequestAPI.swift index bb272d204..f92380f95 100644 --- a/SessionMessagingKitTests/_TestUtilities/TestOnionRequestAPI.swift +++ b/SessionMessagingKitTests/_TestUtilities/TestOnionRequestAPI.swift @@ -45,9 +45,13 @@ class TestOnionRequestAPI: OnionRequestAPIType { httpMethod: (request.httpMethod ?? "GET"), headers: (request.allHTTPHeaderFields ?? [:]), body: request.httpBody, - server: server, - version: .v4, - publicKey: x25519PublicKey + destination: OnionRequestAPIDestination.server( + host: (request.url?.host ?? ""), + target: OnionRequestAPIVersion.v4.rawValue, + x25519PublicKey: x25519PublicKey, + scheme: request.url!.scheme, + port: request.url!.port.map { UInt16($0) } + ) ), code: 200, headers: [:] @@ -61,15 +65,11 @@ class TestOnionRequestAPI: OnionRequestAPIType { static func sendOnionRequest(_ payload: Data, to snode: Snode) -> AnyPublisher<(ResponseInfoType, Data?), Error> { let responseInfo: ResponseInfo = ResponseInfo( requestData: RequestData( - urlString: nil, + urlString: "\(snode.address):\(snode.port)/onion_req/v2", httpMethod: "POST", headers: [:], - snodeMethod: nil, body: payload, - - server: "", - version: .v3, - publicKey: snode.x25519PublicKey + destination: OnionRequestAPIDestination.snode(snode) ), code: 200, headers: [:] diff --git a/SessionSnodeKit/Networking/OnionRequestAPI+Encryption.swift b/SessionSnodeKit/Networking/OnionRequestAPI+Encryption.swift index 4d39e5954..86f25c0fd 100644 --- a/SessionSnodeKit/Networking/OnionRequestAPI+Encryption.swift +++ b/SessionSnodeKit/Networking/OnionRequestAPI+Encryption.swift @@ -2,7 +2,7 @@ import Foundation import Combine -import CryptoSwift +import CryptoKit import SessionUtilitiesKit internal extension OnionRequestAPI { @@ -28,14 +28,14 @@ internal extension OnionRequestAPI { static func encrypt( _ payload: Data, for destination: OnionRequestAPIDestination - ) -> AnyPublisher { + ) -> AnyPublisher { switch destination { case .snode(let snode): // Need to wrap the payload for snode requests return encode(ciphertext: payload, json: [ "headers" : "" ]) - .flatMap { data -> AnyPublisher in + .flatMap { data -> AnyPublisher in do { - return Just(try AESGCM.encrypt(data, for: snode.x25519PublicKey)) + return Just(try AES.GCM.encrypt(data, for: snode.x25519PublicKey)) .setFailureType(to: Error.self) .eraseToAnyPublisher() } @@ -48,7 +48,7 @@ internal extension OnionRequestAPI { case .server(_, _, let serverX25519PublicKey, _, _): do { - return Just(try AESGCM.encrypt(payload, for: serverX25519PublicKey)) + return Just(try AES.GCM.encrypt(payload, for: serverX25519PublicKey)) .setFailureType(to: Error.self) .eraseToAnyPublisher() } @@ -63,8 +63,8 @@ internal extension OnionRequestAPI { static func encryptHop( from lhs: OnionRequestAPIDestination, to rhs: OnionRequestAPIDestination, - using previousEncryptionResult: AESGCM.EncryptionResult - ) -> AnyPublisher { + using previousEncryptionResult: AES.GCM.EncryptionResult + ) -> AnyPublisher { var parameters: JSON switch rhs { @@ -89,9 +89,9 @@ internal extension OnionRequestAPI { }() return encode(ciphertext: previousEncryptionResult.ciphertext, json: parameters) - .flatMap { data -> AnyPublisher in + .flatMap { data -> AnyPublisher in do { - return Just(try AESGCM.encrypt(data, for: x25519PublicKey)) + return Just(try AES.GCM.encrypt(data, for: x25519PublicKey)) .setFailureType(to: Error.self) .eraseToAnyPublisher() } diff --git a/SessionSnodeKit/Networking/OnionRequestAPI.swift b/SessionSnodeKit/Networking/OnionRequestAPI.swift index f3cabbee1..e078abe1e 100644 --- a/SessionSnodeKit/Networking/OnionRequestAPI.swift +++ b/SessionSnodeKit/Networking/OnionRequestAPI.swift @@ -2,7 +2,7 @@ import Foundation import Combine -import CryptoSwift +import CryptoKit import GRDB import SessionUtilitiesKit @@ -51,7 +51,7 @@ public enum OnionRequestAPI: OnionRequestAPIType { // MARK: - Onion Building Result - private typealias OnionBuildingResult = (guardSnode: Snode, finalEncryptionResult: AESGCM.EncryptionResult, destinationSymmetricKey: Data) + private typealias OnionBuildingResult = (guardSnode: Snode, finalEncryptionResult: AES.GCM.EncryptionResult, destinationSymmetricKey: Data) // MARK: - Private API @@ -359,18 +359,18 @@ public enum OnionRequestAPI: OnionRequestAPIType { ) -> AnyPublisher { var guardSnode: Snode! var targetSnodeSymmetricKey: Data! // Needed by invoke(_:on:with:) to decrypt the response sent back by the destination - var encryptionResult: AESGCM.EncryptionResult! + var encryptionResult: AES.GCM.EncryptionResult! var snodeToExclude: Snode? if case .snode(let snode) = destination { snodeToExclude = snode } return getPath(excluding: snodeToExclude) - .flatMap { path -> AnyPublisher in + .flatMap { path -> AnyPublisher in guardSnode = path.first! // Encrypt in reverse order, i.e. the destination first return encrypt(payload, for: destination) - .flatMap { r -> AnyPublisher in + .flatMap { r -> AnyPublisher in targetSnodeSymmetricKey = r.symmetricKey // Recursively encrypt the layers of the onion (again in reverse order) @@ -378,7 +378,7 @@ public enum OnionRequestAPI: OnionRequestAPIType { var path = path var rhs = destination - func addLayer() -> AnyPublisher { + func addLayer() -> AnyPublisher { guard !path.isEmpty else { return Just(encryptionResult) .setFailureType(to: Error.self) @@ -388,7 +388,7 @@ public enum OnionRequestAPI: OnionRequestAPIType { let lhs = OnionRequestAPIDestination.snode(path.removeLast()) return OnionRequestAPI .encryptHop(from: lhs, to: rhs, using: encryptionResult) - .flatMap { r -> AnyPublisher in + .flatMap { r -> AnyPublisher in encryptionResult = r rhs = lhs return addLayer() @@ -644,13 +644,13 @@ public enum OnionRequestAPI: OnionRequestAPIType { .eraseToAnyPublisher() } - guard let base64EncodedIVAndCiphertext = json["result"] as? String, let ivAndCiphertext = Data(base64Encoded: base64EncodedIVAndCiphertext), ivAndCiphertext.count >= AESGCM.ivSize else { + guard let base64EncodedIVAndCiphertext = json["result"] as? String, let ivAndCiphertext = Data(base64Encoded: base64EncodedIVAndCiphertext), ivAndCiphertext.count >= AES.GCM.ivSize else { return Fail(error: HTTPError.invalidJSON) .eraseToAnyPublisher() } do { - let data = try AESGCM.decrypt(ivAndCiphertext, with: destinationSymmetricKey) + let data = try AES.GCM.decrypt(ivAndCiphertext, with: destinationSymmetricKey) guard let json = try JSONSerialization.jsonObject(with: data, options: [ .fragmentsAllowed ]) as? JSON, let statusCode = json["status_code"] as? Int ?? json["status"] as? Int else { return Fail(error: HTTPError.invalidJSON) @@ -725,13 +725,13 @@ public enum OnionRequestAPI: OnionRequestAPIType { // V4 Onion Requests have a very different structure for responses case .v4: - guard responseData.count >= AESGCM.ivSize else { + guard responseData.count >= AES.GCM.ivSize else { return Fail(error: HTTPError.invalidResponse) .eraseToAnyPublisher() } do { - let data: Data = try AESGCM.decrypt(responseData, with: destinationSymmetricKey) + let data: Data = try AES.GCM.decrypt(responseData, with: destinationSymmetricKey) // Process the bencoded response guard let processedResponse: (info: ResponseInfoType, body: Data?) = process(bencodedData: data) else { diff --git a/SessionSnodeKit/Types/OnionRequestAPIDestination.swift b/SessionSnodeKit/Types/OnionRequestAPIDestination.swift index 8483ce347..235bb817e 100644 --- a/SessionSnodeKit/Types/OnionRequestAPIDestination.swift +++ b/SessionSnodeKit/Types/OnionRequestAPIDestination.swift @@ -2,7 +2,7 @@ import Foundation -public enum OnionRequestAPIDestination: CustomStringConvertible { +public enum OnionRequestAPIDestination: CustomStringConvertible, Codable { case snode(Snode) case server(host: String, target: String, x25519PublicKey: String, scheme: String?, port: UInt16?) diff --git a/SessionTests/Conversations/Settings/ThreadDisappearingMessagesViewModelSpec.swift b/SessionTests/Conversations/Settings/ThreadDisappearingMessagesViewModelSpec.swift index 062114944..5bf5be0a5 100644 --- a/SessionTests/Conversations/Settings/ThreadDisappearingMessagesViewModelSpec.swift +++ b/SessionTests/Conversations/Settings/ThreadDisappearingMessagesViewModelSpec.swift @@ -7,8 +7,8 @@ import Nimble @testable import Session -class ThreadDisappearingMessagesViewModelSpec: QuickSpec { - typealias ParentType = SessionTableViewModel +class ThreadDisappearingMessagesSettingsViewModelSpec: QuickSpec { + typealias ParentType = SessionTableViewModel // MARK: - Spec @@ -16,9 +16,9 @@ class ThreadDisappearingMessagesViewModelSpec: QuickSpec { var mockStorage: Storage! var cancellables: [AnyCancellable] = [] var dependencies: Dependencies! - var viewModel: ThreadDisappearingMessagesViewModel! + var viewModel: ThreadDisappearingMessagesSettingsViewModel! - describe("a ThreadDisappearingMessagesViewModel") { + describe("a ThreadDisappearingMessagesSettingsViewModel") { // MARK: - Configuration beforeEach { @@ -41,17 +41,17 @@ class ThreadDisappearingMessagesViewModelSpec: QuickSpec { variant: .contact ).insert(db) } - viewModel = ThreadDisappearingMessagesViewModel( + viewModel = ThreadDisappearingMessagesSettingsViewModel( dependencies: dependencies, threadId: "TestId", config: DisappearingMessagesConfiguration.defaultWith("TestId") ) cancellables.append( - viewModel.observableSettingsData + viewModel.observableTableData .receiveOnMain(immediately: true) .sink( receiveCompletion: { _ in }, - receiveValue: { viewModel.updateSettings($0) } + receiveValue: { viewModel.updateTableData($0.0) } ) ) } @@ -72,18 +72,18 @@ class ThreadDisappearingMessagesViewModelSpec: QuickSpec { } it("has the correct number of items") { - expect(viewModel.settingsData.count) + expect(viewModel.tableData.count) .to(equal(1)) - expect(viewModel.settingsData.first?.elements.count) + expect(viewModel.tableData.first?.elements.count) .to(equal(12)) } it("has the correct default state") { - expect(viewModel.settingsData.first?.elements.first) + expect(viewModel.tableData.first?.elements.first) .to( equal( SessionCell.Info( - id: ThreadDisappearingMessagesViewModel.Item( + id: ThreadDisappearingMessagesSettingsViewModel.Item( title: "DISAPPEARING_MESSAGES_OFF".localized() ), position: .top, @@ -98,7 +98,7 @@ class ThreadDisappearingMessagesViewModelSpec: QuickSpec { let title: String = (DisappearingMessagesConfiguration.validDurationsSeconds.last? .formatted(format: .long)) .defaulting(to: "") - expect(viewModel.settingsData.first?.elements.last) + expect(viewModel.tableData.first?.elements.last) .to( equal( SessionCell.Info( @@ -123,25 +123,25 @@ class ThreadDisappearingMessagesViewModelSpec: QuickSpec { mockStorage.write { db in _ = try config.saved(db) } - viewModel = ThreadDisappearingMessagesViewModel( + viewModel = ThreadDisappearingMessagesSettingsViewModel( dependencies: dependencies, threadId: "TestId", config: config ) cancellables.append( - viewModel.observableSettingsData + viewModel.observableTableData .receiveOnMain(immediately: true) .sink( receiveCompletion: { _ in }, - receiveValue: { viewModel.updateSettings($0) } + receiveValue: { viewModel.updateTableData($0.0) } ) ) - expect(viewModel.settingsData.first?.elements.first) + expect(viewModel.tableData.first?.elements.first) .to( equal( SessionCell.Info( - id: ThreadDisappearingMessagesViewModel.Item( + id: ThreadDisappearingMessagesSettingsViewModel.Item( title: "DISAPPEARING_MESSAGES_OFF".localized() ), position: .top, @@ -156,7 +156,7 @@ class ThreadDisappearingMessagesViewModelSpec: QuickSpec { let title: String = (DisappearingMessagesConfiguration.validDurationsSeconds.last? .formatted(format: .long)) .defaulting(to: "") - expect(viewModel.settingsData.first?.elements.last) + expect(viewModel.tableData.first?.elements.last) .to( equal( SessionCell.Info( diff --git a/SessionTests/Conversations/Settings/ThreadSettingsViewModelSpec.swift b/SessionTests/Conversations/Settings/ThreadSettingsViewModelSpec.swift index 645fe5ac4..02b7f9817 100644 --- a/SessionTests/Conversations/Settings/ThreadSettingsViewModelSpec.swift +++ b/SessionTests/Conversations/Settings/ThreadSettingsViewModelSpec.swift @@ -69,10 +69,6 @@ class ThreadSettingsViewModelSpec: QuickSpec { didTriggerSearchCallbackTriggered = true } ) - setupStandardBinding() - } - - func setupStandardBinding() { disposables.append( viewModel.observableTableData .receiveOnMain(immediately: true) @@ -81,14 +77,6 @@ class ThreadSettingsViewModelSpec: QuickSpec { receiveValue: { viewModel.updateTableData($0.0) } ) ) - disposables.append( - viewModel.transitionToScreen - .receiveOnMain(immediately: true) - .sink( - receiveCompletion: { _ in }, - receiveValue: { transitionInfo = $0 } - ) - ) } afterEach { @@ -105,7 +93,7 @@ class ThreadSettingsViewModelSpec: QuickSpec { context("with any conversation type") { it("triggers the search callback when tapping search") { - viewModel.settingsData + viewModel.tableData .first(where: { $0.model == .content })? .elements .first(where: { $0.id == .searchConversation })? @@ -115,10 +103,10 @@ class ThreadSettingsViewModelSpec: QuickSpec { } it("mutes a conversation") { - viewModel.settingsData + viewModel.tableData .first(where: { $0.model == .content })? .elements - .first(where: { $0.id == .notifications })? + .first(where: { $0.id == .notificationMute })? .onTap?() expect( @@ -145,11 +133,11 @@ class ThreadSettingsViewModelSpec: QuickSpec { ) .toNot(beNil()) - viewModel.settingsData + viewModel.tableData .first(where: { $0.model == .content })? .elements .first(where: { $0.id == .notificationMute })? - .onTap?(nil) + .onTap?() expect( mockStorage @@ -179,12 +167,12 @@ class ThreadSettingsViewModelSpec: QuickSpec { didTriggerSearchCallbackTriggered = true } ) - cancellables.append( - viewModel.observableSettingsData + disposables.append( + viewModel.observableTableData .receiveOnMain(immediately: true) .sink( receiveCompletion: { _ in }, - receiveValue: { viewModel.updateSettings($0) } + receiveValue: { viewModel.updateTableData($0.0) } ) ) } @@ -210,7 +198,7 @@ class ThreadSettingsViewModelSpec: QuickSpec { it("has no mute button") { expect( - viewModel.settingsData + viewModel.tableData .first(where: { $0.model == .content })? .elements .first(where: { $0.id == .notificationMute }) @@ -233,7 +221,7 @@ class ThreadSettingsViewModelSpec: QuickSpec { ParentType.NavItem( id: .cancel, systemItem: .cancel, - accessibilityIdentifier: "Cancel" + accessibilityIdentifier: "Cancel button" ) ])) expect(viewModel.rightNavItems.firstValue()) @@ -453,12 +441,12 @@ class ThreadSettingsViewModelSpec: QuickSpec { didTriggerSearchCallbackTriggered = true } ) - cancellables.append( - viewModel.observableSettingsData + disposables.append( + viewModel.observableTableData .receiveOnMain(immediately: true) .sink( receiveCompletion: { _ in }, - receiveValue: { viewModel.updateSettings($0) } + receiveValue: { viewModel.updateTableData($0.0) } ) ) } @@ -495,12 +483,12 @@ class ThreadSettingsViewModelSpec: QuickSpec { didTriggerSearchCallbackTriggered = true } ) - cancellables.append( - viewModel.observableSettingsData + disposables.append( + viewModel.observableTableData .receiveOnMain(immediately: true) .sink( receiveCompletion: { _ in }, - receiveValue: { viewModel.updateSettings($0) } + receiveValue: { viewModel.updateTableData($0.0) } ) ) } diff --git a/SessionTests/Settings/NotificationContentViewModelSpec.swift b/SessionTests/Settings/NotificationContentViewModelSpec.swift index 91166959f..04f11d74b 100644 --- a/SessionTests/Settings/NotificationContentViewModelSpec.swift +++ b/SessionTests/Settings/NotificationContentViewModelSpec.swift @@ -30,11 +30,11 @@ class NotificationContentViewModelSpec: QuickSpec { ] ) viewModel = NotificationContentViewModel(storage: mockStorage, scheduling: .immediate) - dataChangeCancellable = viewModel.observableSettingsData + dataChangeCancellable = viewModel.observableTableData .receiveOnMain(immediately: true) .sink( receiveCompletion: { _ in }, - receiveValue: { viewModel.updateSettings($0) } + receiveValue: { viewModel.updateTableData($0.0) } ) } @@ -55,14 +55,14 @@ class NotificationContentViewModelSpec: QuickSpec { } it("has the correct number of items") { - expect(viewModel.settingsData.count) + expect(viewModel.tableData.count) .to(equal(1)) - expect(viewModel.settingsData.first?.elements.count) + expect(viewModel.tableData.first?.elements.count) .to(equal(3)) } it("has the correct default state") { - expect(viewModel.settingsData.first?.elements) + expect(viewModel.tableData.first?.elements) .to( equal([ SessionCell.Info( @@ -98,14 +98,14 @@ class NotificationContentViewModelSpec: QuickSpec { db[.preferencesNotificationPreviewType] = Preferences.NotificationPreviewType.nameNoPreview } viewModel = NotificationContentViewModel(storage: mockStorage, scheduling: .immediate) - dataChangeCancellable = viewModel.observableSettingsData + dataChangeCancellable = viewModel.observableTableData .receiveOnMain(immediately: true) .sink( receiveCompletion: { _ in }, - receiveValue: { viewModel.updateSettings($0) } + receiveValue: { viewModel.updateTableData($0.0) } ) - expect(viewModel.settingsData.first?.elements) + expect(viewModel.tableData.first?.elements) .to( equal([ SessionCell.Info( diff --git a/SessionUtilitiesKit/Crypto/AESGCM.swift b/SessionUtilitiesKit/Crypto/AESGCM.swift deleted file mode 100644 index e20c8987d..000000000 --- a/SessionUtilitiesKit/Crypto/AESGCM.swift +++ /dev/null @@ -1,77 +0,0 @@ -import CryptoSwift -import Curve25519Kit - -public enum AESGCM { - public static let gcmTagSize: UInt = 16 - public static let ivSize: UInt = 12 - - public struct EncryptionResult { public let ciphertext: Data, symmetricKey: Data, ephemeralPublicKey: Data } - - public enum Error : LocalizedError { - case keyPairGenerationFailed - case sharedSecretGenerationFailed - - public var errorDescription: String? { - switch self { - case .keyPairGenerationFailed: return "Couldn't generate a key pair." - case .sharedSecretGenerationFailed: return "Couldn't generate a shared secret." - } - } - } - - /// - Note: Sync. Don't call from the main thread. - public static func generateSymmetricKey(x25519PublicKey: Data, x25519PrivateKey: Data) throws -> Data { - if Thread.isMainThread { - #if DEBUG - preconditionFailure("It's illegal to call encrypt(_:forSnode:) from the main thread.") - #endif - } - guard let sharedSecret = try? Curve25519.generateSharedSecret(fromPublicKey: x25519PublicKey, privateKey: x25519PrivateKey) else { - throw Error.sharedSecretGenerationFailed - } - let salt = "LOKI" - return try Data(HMAC(key: salt.bytes, variant: .sha256).authenticate(sharedSecret.bytes)) - } - - /// - Note: Sync. Don't call from the main thread. - public static func decrypt(_ ivAndCiphertext: Data, with symmetricKey: Data) throws -> Data { - if Thread.isMainThread { - #if DEBUG - preconditionFailure("It's illegal to call decrypt(_:usingAESGCMWithSymmetricKey:) from the main thread.") - #endif - } - let iv = ivAndCiphertext[0.. Data { - if Thread.isMainThread { - #if DEBUG - preconditionFailure("It's illegal to call encrypt(_:usingAESGCMWithSymmetricKey:) from the main thread.") - #endif - } - let iv = Data.getSecureRandomData(ofSize: ivSize)! - let gcm = GCM(iv: iv.bytes, tagLength: Int(gcmTagSize), mode: .combined) - let aes = try AES(key: symmetricKey.bytes, blockMode: gcm, padding: .noPadding) - let ciphertext = try aes.encrypt(plaintext.bytes) - return iv + Data(ciphertext) - } - - /// - Note: Sync. Don't call from the main thread. - public static func encrypt(_ plaintext: Data, for hexEncodedX25519PublicKey: String) throws -> EncryptionResult { - if Thread.isMainThread { - #if DEBUG - preconditionFailure("It's illegal to call encrypt(_:forSnode:) from the main thread.") - #endif - } - let x25519PublicKey = Data(hex: hexEncodedX25519PublicKey) - let ephemeralKeyPair = Curve25519.generateKeyPair() - let symmetricKey = try generateSymmetricKey(x25519PublicKey: x25519PublicKey, x25519PrivateKey: ephemeralKeyPair.privateKey) - let ciphertext = try encrypt(plaintext, with: Data(symmetricKey)) - return EncryptionResult(ciphertext: ciphertext, symmetricKey: Data(symmetricKey), ephemeralPublicKey: ephemeralKeyPair.publicKey) - } -} diff --git a/SessionUtilitiesKit/Crypto/CryptoKit+Utilities.swift b/SessionUtilitiesKit/Crypto/CryptoKit+Utilities.swift new file mode 100644 index 000000000..c3af90b73 --- /dev/null +++ b/SessionUtilitiesKit/Crypto/CryptoKit+Utilities.swift @@ -0,0 +1,109 @@ +// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. + +import Foundation +import CryptoKit +import Curve25519Kit + +public extension Digest { + var bytes: [UInt8] { Array(makeIterator()) } + var data: Data { Data(bytes) } + + var hexString: String { + bytes.map { String(format: "%02X", $0) }.joined() + } +} + +// MARK: - AES.GCM + +public extension AES.GCM { + static let ivSize: Int = 12 + + struct EncryptionResult { + public let ciphertext: Data + public let symmetricKey: Data + public let ephemeralPublicKey: Data + } + + enum Error: LocalizedError { + case keyPairGenerationFailed + case sharedSecretGenerationFailed + + public var errorDescription: String? { + switch self { + case .keyPairGenerationFailed: return "Couldn't generate a key pair." + case .sharedSecretGenerationFailed: return "Couldn't generate a shared secret." + } + } + } + + /// - Note: Sync. Don't call from the main thread. + static func generateSymmetricKey(x25519PublicKey: Data, x25519PrivateKey: Data) throws -> Data { + if Thread.isMainThread { + #if DEBUG + preconditionFailure("It's illegal to call encrypt(_:forSnode:) from the main thread.") + #endif + } + guard let sharedSecret: Data = try? Curve25519.generateSharedSecret(fromPublicKey: x25519PublicKey, privateKey: x25519PrivateKey) else { + throw Error.sharedSecretGenerationFailed + } + let salt = "LOKI" + + return Data( + HMAC.authenticationCode( + for: sharedSecret, + using: SymmetricKey(data: salt.bytes) + ) + ) + } + + /// - Note: Sync. Don't call from the main thread. + static func decrypt(_ nonceAndCiphertext: Data, with symmetricKey: Data) throws -> Data { + if Thread.isMainThread { + #if DEBUG + preconditionFailure("It's illegal to call decrypt(_:usingAESGCMWithSymmetricKey:) from the main thread.") + #endif + } + + return try AES.GCM.open( + try AES.GCM.SealedBox(combined: nonceAndCiphertext), + using: SymmetricKey(data: symmetricKey) + ) + } + + /// - Note: Sync. Don't call from the main thread. + static func encrypt(_ plaintext: Data, with symmetricKey: Data) throws -> Data { + if Thread.isMainThread { + #if DEBUG + preconditionFailure("It's illegal to call encrypt(_:usingAESGCMWithSymmetricKey:) from the main thread.") + #endif + } + + let nonceData: Data = try Randomness.generateRandomBytes(numberBytes: ivSize) + let sealedData: AES.GCM.SealedBox = try AES.GCM.seal( + plaintext, + using: SymmetricKey(data: symmetricKey), + nonce: try AES.GCM.Nonce(data: nonceData) + ) + + guard let cipherText: Data = sealedData.combined else { + throw GeneralError.keyGenerationFailed + } + + return cipherText + } + + /// - Note: Sync. Don't call from the main thread. + static func encrypt(_ plaintext: Data, for hexEncodedX25519PublicKey: String) throws -> EncryptionResult { + if Thread.isMainThread { + #if DEBUG + preconditionFailure("It's illegal to call encrypt(_:forSnode:) from the main thread.") + #endif + } + let x25519PublicKey = Data(hex: hexEncodedX25519PublicKey) + let ephemeralKeyPair = Curve25519.generateKeyPair() + let symmetricKey = try generateSymmetricKey(x25519PublicKey: x25519PublicKey, x25519PrivateKey: ephemeralKeyPair.privateKey) + let ciphertext = try encrypt(plaintext, with: Data(symmetricKey)) + + return EncryptionResult(ciphertext: ciphertext, symmetricKey: Data(symmetricKey), ephemeralPublicKey: ephemeralKeyPair.publicKey) + } +} diff --git a/SessionUtilitiesKit/Crypto/Data+SecureRandom.swift b/SessionUtilitiesKit/Crypto/Data+SecureRandom.swift index 1ca9749f8..fa9b1d7f2 100644 --- a/SessionUtilitiesKit/Crypto/Data+SecureRandom.swift +++ b/SessionUtilitiesKit/Crypto/Data+SecureRandom.swift @@ -1,3 +1,5 @@ +// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. + import Foundation public extension Data { diff --git a/SessionUtilitiesKit/Crypto/DiffieHellman.swift b/SessionUtilitiesKit/Crypto/DiffieHellman.swift deleted file mode 100644 index cbc7b9950..000000000 --- a/SessionUtilitiesKit/Crypto/DiffieHellman.swift +++ /dev/null @@ -1,49 +0,0 @@ -import CryptoSwift -import Curve25519Kit - -public final class DiffieHellman : NSObject { - public static let ivSize: UInt = 16 - - public enum Error : LocalizedError { - case decryptionFailed - case sharedSecretGenerationFailed - - public var errorDescription: String { - switch self { - case .decryptionFailed: return "Couldn't decrypt data" - case .sharedSecretGenerationFailed: return "Couldn't generate a shared secret." - } - } - } - - private override init() { } - - public static func encrypt(_ plaintext: Data, using symmetricKey: Data) throws -> Data { - let iv = Data.getSecureRandomData(ofSize: ivSize)! - let cbc = CBC(iv: iv.bytes) - let aes = try AES(key: symmetricKey.bytes, blockMode: cbc) - let ciphertext = try aes.encrypt(plaintext.bytes) - let ivAndCiphertext = iv.bytes + ciphertext - return Data(ivAndCiphertext) - } - - public static func encrypt(_ plaintext: Data, publicKey: Data, privateKey: Data) throws -> Data { - guard let symmetricKey = try? Curve25519.generateSharedSecret(fromPublicKey: publicKey, privateKey: privateKey) else { throw Error.sharedSecretGenerationFailed } - return try encrypt(plaintext, using: symmetricKey) - } - - public static func decrypt(_ ivAndCiphertext: Data, using symmetricKey: Data) throws -> Data { - guard ivAndCiphertext.count >= ivSize else { throw Error.decryptionFailed } - let iv = ivAndCiphertext[.. Data { - guard let symmetricKey = try? Curve25519.generateSharedSecret(fromPublicKey: publicKey, privateKey: privateKey) else { throw Error.sharedSecretGenerationFailed } - return try decrypt(ivAndCiphertext, using: symmetricKey) - } -} diff --git a/SessionUtilitiesKit/Crypto/ECKeyPair+Hexadecimal.swift b/SessionUtilitiesKit/Crypto/ECKeyPair+Hexadecimal.swift deleted file mode 100644 index 5dd86097d..000000000 --- a/SessionUtilitiesKit/Crypto/ECKeyPair+Hexadecimal.swift +++ /dev/null @@ -1,26 +0,0 @@ -import Curve25519Kit - -public extension ECKeyPair { - - @objc var hexEncodedPrivateKey: String { - return privateKey.map { String(format: "%02hhx", $0) }.joined() - } - - @objc var hexEncodedPublicKey: String { - // Prefixing with 'SessionId.Prefix.standard' is necessary for what seems to be a sort of Signal public key versioning system - return SessionId(.standard, publicKey: publicKey.bytes).hexString - } - - @objc static func isValidHexEncodedPublicKey(candidate: String) -> Bool { - // Note: If the logic in here changes ensure it doesn't break `SessionId.Prefix(from:)` - // Check that it's a valid hexadecimal encoding - guard Hex.isValid(candidate) else { return false } - // Check that it has length 66 and a valid prefix - guard candidate.count == 66 && SessionId.Prefix.allCases.first(where: { candidate.hasPrefix($0.rawValue) }) != nil else { - return false - } - - // It appears to be a valid public key - return true - } -} diff --git a/SessionUtilitiesKit/Crypto/Hex.swift b/SessionUtilitiesKit/Crypto/Hex.swift index b90a916fa..f9b01f406 100644 --- a/SessionUtilitiesKit/Crypto/Hex.swift +++ b/SessionUtilitiesKit/Crypto/Hex.swift @@ -1,8 +1,74 @@ +// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. + +import Foundation public enum Hex { - public static func isValid(_ string: String) -> Bool { let allowedCharacters = CharacterSet(charactersIn: "0123456789ABCDEF") + return string.uppercased().unicodeScalars.allSatisfy { allowedCharacters.contains($0) } } } + +// MARK: - Data + +public extension Data { + var bytes: [UInt8] { return Array(self) } + + func toHexString() -> String { + return bytes.map { String(format: "%02x", $0) }.joined() + } + + init(hex: String) { + self.init(Array(hex: hex)) + } +} + +// MARK: - Array + +public extension Array where Element == UInt8 { + init(hex: String) { + self = Array() + self.reserveCapacity(hex.unicodeScalars.lazy.underestimatedCount) + + var buffer: UInt8? + var skip = (hex.hasPrefix("0x") ? 2 : 0) + + for char in hex.unicodeScalars.lazy { + guard skip == 0 else { + skip -= 1 + continue + } + + guard char.value >= 48 && char.value <= 102 else { + removeAll() + return + } + + let v: UInt8 + let c: UInt8 = UInt8(char.value) + + switch c { + case let c where c <= 57: v = c - 48 + case let c where c >= 65 && c <= 70: v = c - 55 + case let c where c >= 97: v = c - 87 + + default: + removeAll() + return + } + + if let b = buffer { + append(b << 4 | v) + buffer = nil + } + else { + buffer = v + } + } + + if let b = buffer { + append(b) + } + } +} diff --git a/SessionUtilitiesKit/Crypto/KeyPair.swift b/SessionUtilitiesKit/Crypto/KeyPair.swift new file mode 100644 index 000000000..4c7ec0a74 --- /dev/null +++ b/SessionUtilitiesKit/Crypto/KeyPair.swift @@ -0,0 +1,35 @@ +// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. + +import Foundation + +public struct KeyPair { + public let publicKey: [UInt8] + public let secretKey: [UInt8] + + public var hexEncodedPublicKey: String { + return SessionId(.standard, publicKey: publicKey).hexString + } + + // MARK: - Initialization + + public init(publicKey: [UInt8], secretKey: [UInt8]) { + self.publicKey = publicKey + self.secretKey = secretKey + } + + // MARK: - Functions + + public static func isValidHexEncodedPublicKey(candidate: String) -> Bool { + // Note: If the logic in here changes ensure it doesn't break `SessionId.Prefix(from:)` + // Check that it's a valid hexadecimal encoding + guard Hex.isValid(candidate) else { return false } + + // Check that it has length 66 and a valid prefix + guard candidate.count == 66 && SessionId.Prefix.allCases.first(where: { candidate.hasPrefix($0.rawValue) }) != nil else { + return false + } + + // It appears to be a valid public key + return true + } +} diff --git a/SessionUtilitiesKit/Crypto/Mnemonic.swift b/SessionUtilitiesKit/Crypto/Mnemonic.swift index b420a89f7..ac8f46af5 100644 --- a/SessionUtilitiesKit/Crypto/Mnemonic.swift +++ b/SessionUtilitiesKit/Crypto/Mnemonic.swift @@ -1,9 +1,11 @@ +// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. + +import Foundation import CryptoSwift /// Based on [mnemonic.js](https://github.com/loki-project/loki-messenger/blob/development/libloki/modules/mnemonic.js) . public enum Mnemonic { - - public struct Language : Hashable { + public struct Language: Hashable { fileprivate let filename: String fileprivate let prefixLength: UInt @@ -12,8 +14,8 @@ public enum Mnemonic { public static let portuguese = Language(filename: "portuguese", prefixLength: 4) public static let spanish = Language(filename: "spanish", prefixLength: 4) - private static var wordSetCache: [Language:[String]] = [:] - private static var truncatedWordSetCache: [Language:[String]] = [:] + private static var wordSetCache: [Language: [String]] = [:] + private static var truncatedWordSetCache: [Language: [String]] = [:] private init(filename: String, prefixLength: UInt) { self.filename = filename @@ -23,23 +25,25 @@ public enum Mnemonic { fileprivate func loadWordSet() -> [String] { if let cachedResult = Language.wordSetCache[self] { return cachedResult - } else { - let url = Bundle.main.url(forResource: filename, withExtension: "txt")! - let contents = try! String(contentsOf: url) - let result = contents.split(separator: ",").map { String($0) } - Language.wordSetCache[self] = result - return result } + + let url = Bundle.main.url(forResource: filename, withExtension: "txt")! + let contents = try! String(contentsOf: url) + let result = contents.split(separator: ",").map { String($0) } + Language.wordSetCache[self] = result + + return result } fileprivate func loadTruncatedWordSet() -> [String] { if let cachedResult = Language.truncatedWordSetCache[self] { return cachedResult - } else { - let result = loadWordSet().map { $0.prefix(length: prefixLength) } - Language.truncatedWordSetCache[self] = result - return result } + + let result = loadWordSet().map { $0.prefix(length: prefixLength) } + Language.truncatedWordSetCache[self] = result + + return result } } @@ -68,6 +72,7 @@ public enum Mnemonic { var result: [String] = [] let n = wordSet.count let characterCount = string.indices.count // Safe for this particular case + for chunkStartIndexAsInt in stride(from: 0, to: characterCount, by: 8) { let chunkStartIndex = string.index(string.startIndex, offsetBy: chunkStartIndexAsInt) let chunkEndIndex = string.index(chunkStartIndex, offsetBy: 8) @@ -76,6 +81,7 @@ public enum Mnemonic { let p3 = string[chunkEndIndex..= 12 else { throw DecodingError.inputTooShort } guard !words.count.isMultiple(of: 3) else { throw DecodingError.missingLastWord } + // Get checksum word let checksumWord = words.popLast()! + // Decode for chunkStartIndex in stride(from: 0, to: words.count, by: 3) { guard let w1 = truncatedWordSet.firstIndex(of: words[chunkStartIndex].prefix(length: prefixLength)), @@ -112,10 +123,12 @@ public enum Mnemonic { let string = "0000000" + String(x, radix: 16) result += swap(String(string[string.index(string.endIndex, offsetBy: -8).. String.Index { return x.index(x.startIndex, offsetBy: indexAsInt) } + let p1 = x[toStringIndex(6).. Int { let checksum = Array(x.map { $0.prefix(length: prefixLength) }.joined().utf8).crc32() + return Int(checksum) % x.count } } private extension String { - func prefix(length: UInt) -> String { return String(self[startIndex.. String { - return Mnemonic.hash(hexEncodedString: string) - } - - @objc(encodeHexEncodedString:) - public static func encode(hexEncodedString string: String) -> String { - return Mnemonic.encode(hexEncodedString: string) - } -} diff --git a/SessionUtilitiesKit/Database/Migrations/_002_SetupStandardJobs.swift b/SessionUtilitiesKit/Database/Migrations/_002_SetupStandardJobs.swift index a4df73df3..7d085d68c 100644 --- a/SessionUtilitiesKit/Database/Migrations/_002_SetupStandardJobs.swift +++ b/SessionUtilitiesKit/Database/Migrations/_002_SetupStandardJobs.swift @@ -2,7 +2,6 @@ import Foundation import GRDB -import Curve25519Kit /// This migration sets up the standard jobs, since we want these jobs to run before any "once-off" jobs we do this migration /// before running the `YDBToGRDBMigration` diff --git a/SessionUtilitiesKit/Database/Models/Identity.swift b/SessionUtilitiesKit/Database/Models/Identity.swift index 9d08731f5..4eb224273 100644 --- a/SessionUtilitiesKit/Database/Models/Identity.swift +++ b/SessionUtilitiesKit/Database/Models/Identity.swift @@ -4,7 +4,6 @@ import Foundation import GRDB import Sodium import Curve25519Kit -import CryptoSwift public struct Identity: Codable, Identifiable, FetchableRecord, PersistableRecord, TableRecord, ColumnExpressible { public static var databaseTableName: String { "identity" } @@ -39,20 +38,10 @@ public struct Identity: Codable, Identifiable, FetchableRecord, PersistableRecor } } -// MARK: - Convenience - -extension ECKeyPair { - func toData() -> Data { - var targetValue: ECKeyPair = self - - return Data(bytes: &targetValue, count: MemoryLayout.size(ofValue: targetValue)) - } -} - // MARK: - GRDB Interactions public extension Identity { - static func generate(from seed: Data) throws -> (ed25519KeyPair: Sign.KeyPair, x25519KeyPair: ECKeyPair) { + static func generate(from seed: Data) throws -> (ed25519KeyPair: KeyPair, x25519KeyPair: KeyPair) { assert(seed.count == 16) let padding = Data(repeating: 0, count: 16) @@ -64,18 +53,25 @@ public extension Identity { throw GeneralError.keyGenerationFailed } - let x25519KeyPair = try ECKeyPair(publicKeyData: Data(x25519PublicKey), privateKeyData: Data(x25519SecretKey)) - - return (ed25519KeyPair: ed25519KeyPair, x25519KeyPair: x25519KeyPair) + return ( + ed25519KeyPair: KeyPair( + publicKey: ed25519KeyPair.publicKey, + secretKey: ed25519KeyPair.secretKey + ), + x25519KeyPair: KeyPair( + publicKey: x25519PublicKey, + secretKey: x25519SecretKey + ) + ) } - static func store(seed: Data, ed25519KeyPair: Sign.KeyPair, x25519KeyPair: ECKeyPair) { + static func store(seed: Data, ed25519KeyPair: KeyPair, x25519KeyPair: KeyPair) { Storage.shared.write { db in try Identity(variant: .seed, data: seed).save(db) try Identity(variant: .ed25519SecretKey, data: Data(ed25519KeyPair.secretKey)).save(db) try Identity(variant: .ed25519PublicKey, data: Data(ed25519KeyPair.publicKey)).save(db) - try Identity(variant: .x25519PrivateKey, data: x25519KeyPair.privateKey).save(db) - try Identity(variant: .x25519PublicKey, data: x25519KeyPair.publicKey).save(db) + try Identity(variant: .x25519PrivateKey, data: Data(x25519KeyPair.secretKey)).save(db) + try Identity(variant: .x25519PublicKey, data: Data(x25519KeyPair.publicKey)).save(db) } } @@ -153,16 +149,3 @@ public extension Identity { NotificationCenter.default.post(name: .registrationStateDidChange, object: nil, userInfo: nil) } } - -// MARK: - Objective-C Support - -// TODO: Remove this when possible -@objc(SUKIdentity) -public class SUKIdentity: NSObject { - @objc(userExists) - public static func userExists() -> Bool { - return Storage.shared - .read { db in Identity.userExists(db) } - .defaulting(to: false) - } -} diff --git a/SessionUtilitiesKit/General/General.swift b/SessionUtilitiesKit/General/General.swift index 2931e47c2..e78c33a89 100644 --- a/SessionUtilitiesKit/General/General.swift +++ b/SessionUtilitiesKit/General/General.swift @@ -2,7 +2,6 @@ import Foundation import GRDB -import Curve25519Kit public protocol GeneralCacheType { var encodedPublicKey: String? { get set } diff --git a/SessionUtilitiesKit/General/SessionId.swift b/SessionUtilitiesKit/General/SessionId.swift index 4e892d489..c9391c6c7 100644 --- a/SessionUtilitiesKit/General/SessionId.swift +++ b/SessionUtilitiesKit/General/SessionId.swift @@ -2,7 +2,6 @@ import Foundation import Sodium -import Curve25519Kit public struct SessionId { public static let byteCount: Int = 33 @@ -21,7 +20,7 @@ public struct SessionId { return } - guard ECKeyPair.isValidHexEncodedPublicKey(candidate: stringValue) else { return nil } + guard KeyPair.isValidHexEncodedPublicKey(candidate: stringValue) else { return nil } guard let targetPrefix: Prefix = Prefix(rawValue: String(stringValue.prefix(2))) else { return nil } self = targetPrefix diff --git a/SignalUtilitiesKit/Profile Pictures/PlaceholderIcon.swift b/SignalUtilitiesKit/Profile Pictures/PlaceholderIcon.swift index 32f0c60f4..d0911e4e8 100644 --- a/SignalUtilitiesKit/Profile Pictures/PlaceholderIcon.swift +++ b/SignalUtilitiesKit/Profile Pictures/PlaceholderIcon.swift @@ -1,9 +1,10 @@ // Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. import UIKit -import CryptoSwift +import CryptoKit import SessionUIKit import SignalCoreKit +import SessionUtilitiesKit public class PlaceholderIcon { private let seed: Int @@ -19,7 +20,10 @@ public class PlaceholderIcon { convenience init(seed: String, colors: [UIColor]? = nil) { // Ensure we have a correct hash var hash = seed - if (hash.matches("^[0-9A-Fa-f]+$") && hash.count >= 12) { hash = seed.sha512() } + + if (hash.matches("^[0-9A-Fa-f]+$") && hash.count >= 12) { + hash = SHA512.hash(data: Data(seed.bytes)).hexString + } guard let number = Int(hash.substring(to: 12), radix: 16) else { owsFailDebug("Failed to generate number from seed string: \(seed).")