From 2a4977d26953b1f62ba1e5e544388401dc7cb009 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 10 Dec 2020 16:12:22 +1100 Subject: [PATCH 1/9] Implement Session protocol --- Podfile | 1 + Podfile.lock | 2 +- .../Database/Storage+Shared.swift | 11 +++++ .../MessageReceiver+Decryption.swift | 43 +++++++++++++++++-- .../Sending & Receiving/MessageReceiver.swift | 29 ++++++++++--- .../MessageSender+Encryption.swift | 16 ++++++- .../Sending & Receiving/MessageSender.swift | 12 ++++-- SessionMessagingKit/Storage.swift | 2 + .../Utilities/Sodium+Conversion.swift | 0 SessionUtilitiesKit/String+SSK.swift | 1 + SessionUtilitiesKit/UIView+OWS.m | 1 - Signal.xcodeproj/project.pbxproj | 6 +-- 12 files changed, 106 insertions(+), 18 deletions(-) rename {Session => SessionMessagingKit}/Utilities/Sodium+Conversion.swift (100%) diff --git a/Podfile b/Podfile index 59d3020d3..40b9f4a76 100644 --- a/Podfile +++ b/Podfile @@ -71,6 +71,7 @@ target 'SessionMessagingKit' do pod 'Reachability', :inhibit_warnings => true pod 'SAMKeychain', :inhibit_warnings => true pod 'SignalCoreKit', git: 'https://github.com/signalapp/SignalCoreKit.git', :inhibit_warnings => true + pod 'Sodium', :inhibit_warnings => true pod 'SwiftProtobuf', '~> 1.5.0', :inhibit_warnings => true pod 'YapDatabase/SQLCipher', :git => 'https://github.com/loki-project/session-ios-yap-database.git', branch: 'signal-release', :inhibit_warnings => true end diff --git a/Podfile.lock b/Podfile.lock index e843a6e1c..caf962654 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -230,6 +230,6 @@ SPEC CHECKSUMS: YYImage: 6db68da66f20d9f169ceb94dfb9947c3867b9665 ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: 7699c2a380fc803ef7f51157f1f75da756aa3b45 +PODFILE CHECKSUM: 3263ab95f60e220882ca53cca4c6bdc2e7a80381 COCOAPODS: 1.10.0.rc.1 diff --git a/SessionMessagingKit/Database/Storage+Shared.swift b/SessionMessagingKit/Database/Storage+Shared.swift index 4ceec0b9a..19ee7a1a6 100644 --- a/SessionMessagingKit/Database/Storage+Shared.swift +++ b/SessionMessagingKit/Database/Storage+Shared.swift @@ -1,4 +1,5 @@ import PromiseKit +import Sodium extension Storage { @@ -23,6 +24,16 @@ extension Storage { public func getUserKeyPair() -> ECKeyPair? { return OWSIdentityManager.shared().identityKeyPair() } + + public func getUserED25519KeyPair() -> Box.KeyPair? { + let dbConnection = OWSIdentityManager.shared().dbConnection + let collection = OWSPrimaryStorageIdentityKeyStoreCollection + guard let hexEncodedPublicKey = dbConnection.object(forKey: LKED25519PublicKey, inCollection: collection) as? String, + let hexEncodedSecretKey = dbConnection.object(forKey: LKED25519SecretKey, inCollection: collection) as? String else { return nil } + let publicKey = Box.KeyPair.PublicKey(hex: hexEncodedPublicKey) + let secretKey = Box.KeyPair.SecretKey(hex: hexEncodedSecretKey) + return Box.KeyPair(publicKey: publicKey, secretKey: secretKey) + } public func getUserDisplayName() -> String? { return SSKEnvironment.shared.profileManager.localProfileName() diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift index b6d4fd744..ea502640b 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift @@ -1,6 +1,7 @@ import CryptoSwift import SessionProtocolKit import SessionUtilitiesKit +import Sodium internal extension MessageReceiver { @@ -8,7 +9,7 @@ internal extension MessageReceiver { let storage = SNMessagingKitConfiguration.shared.signalStorage let certificateValidator = SNMessagingKitConfiguration.shared.certificateValidator guard let data = envelope.content else { throw Error.noData } - guard let userPublicKey = SNMessagingKitConfiguration.shared.storage.getUserPublicKey() else { throw Error.noUserPublicKey } + guard let userPublicKey = SNMessagingKitConfiguration.shared.storage.getUserPublicKey() else { throw Error.noUserX25519KeyPair } let cipher = try SMKSecretSessionCipher(sessionResetImplementation: SNMessagingKitConfiguration.shared.sessionRestorationImplementation, sessionStore: storage, preKeyStore: storage, signedPreKeyStore: storage, identityStore: SNMessagingKitConfiguration.shared.identityKeyStore) let result = try cipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator, cipherTextData: data, @@ -16,6 +17,42 @@ internal extension MessageReceiver { return (result.paddedPayload, result.senderRecipientId) } + static func decryptWithSessionProtocol(envelope: SNProtoEnvelope) throws -> (plaintext: Data, senderX25519PublicKey: String) { + guard let ciphertext = envelope.content else { throw Error.noData } + let recipientX25519PrivateKey: Data + let recipientX25519PublicKey: Data + switch envelope.type { + case .unidentifiedSender: + guard let userX25519KeyPair = SNMessagingKitConfiguration.shared.storage.getUserKeyPair() else { throw Error.noUserX25519KeyPair } + recipientX25519PrivateKey = userX25519KeyPair.privateKey + recipientX25519PublicKey = Data(hex: userX25519KeyPair.hexEncodedPublicKey.removing05PrefixIfNeeded()) + case .closedGroupCiphertext: + guard let hexEncodedGroupPublicKey = envelope.source, SNMessagingKitConfiguration.shared.storage.isClosedGroup(hexEncodedGroupPublicKey) else { throw Error.invalidGroupPublicKey } + guard let hexEncodedGroupPrivateKey = SNMessagingKitConfiguration.shared.storage.getClosedGroupPrivateKey(for: hexEncodedGroupPublicKey) else { throw Error.noGroupPrivateKey } + recipientX25519PrivateKey = Data(hex: hexEncodedGroupPrivateKey) + recipientX25519PublicKey = Data(hex: hexEncodedGroupPublicKey.removing05PrefixIfNeeded()) + default: preconditionFailure() + } + let sodium = Sodium() + let signatureSize = sodium.sign.Bytes + let ed25519PublicKeySize = sodium.sign.PublicKeyBytes + + // 1. ) Decrypt the message + guard let plaintextWithMetadata = sodium.box.open(anonymousCipherText: Bytes(ciphertext), recipientPublicKey: Box.PublicKey(Bytes(recipientX25519PublicKey)), + recipientSecretKey: Bytes(recipientX25519PrivateKey)), plaintextWithMetadata.count > (signatureSize + ed25519PublicKeySize) else { throw Error.decryptionFailed } + // 2. ) Get the message parts + let signature = Bytes(plaintextWithMetadata[plaintextWithMetadata.count - signatureSize ..< plaintextWithMetadata.count]) + let senderED25519PublicKey = Bytes(plaintextWithMetadata[plaintextWithMetadata.count - (signatureSize + ed25519PublicKeySize) ..< plaintextWithMetadata.count - signatureSize]) + let plaintext = Bytes(plaintextWithMetadata[0.. (plaintext: Data, senderPublicKey: String) { // 1. ) Check preconditions guard let groupPublicKey = envelope.source, SNMessagingKitConfiguration.shared.storage.isClosedGroup(groupPublicKey) else { @@ -36,8 +73,8 @@ internal extension MessageReceiver { guard let ephemeralSharedSecret = try? Curve25519.generateSharedSecret(fromPublicKey: ephemeralPublicKey, privateKey: groupPrivateKey) else { throw Error.sharedSecretGenerationFailed } - let salt = "LOKI" - let symmetricKey = try HMAC(key: salt.bytes, variant: .sha256).authenticate(ephemeralSharedSecret.bytes) + let salt = "LOKI".data(using: String.Encoding.utf8, allowLossyConversion: true)!.bytes + let symmetricKey = try HMAC(key: salt, variant: .sha256).authenticate(ephemeralSharedSecret.bytes) let closedGroupCiphertextMessageAsData = try AESGCM.decrypt(ivAndCiphertext, with: Data(symmetricKey)) // 4. ) Parse the closed group ciphertext message let closedGroupCiphertextMessage = ClosedGroupCiphertextMessage(_throws_with: closedGroupCiphertextMessageAsData) diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift index 780383c75..d46d9ae67 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift @@ -7,11 +7,14 @@ public enum MessageReceiver { case invalidMessage case unknownMessage case unknownEnvelopeType - case noUserPublicKey + case noUserX25519KeyPair + case noUserED25519KeyPair + case invalidSignature case noData case senderBlocked case noThread case selfSend + case decryptionFailed // Shared sender keys case invalidGroupPublicKey case noGroupPrivateKey @@ -19,7 +22,7 @@ public enum MessageReceiver { public var isRetryable: Bool { switch self { - case .duplicateMessage, .invalidMessage, .unknownMessage, .unknownEnvelopeType, .noData, .senderBlocked, .selfSend: return false + case .duplicateMessage, .invalidMessage, .unknownMessage, .unknownEnvelopeType, .invalidSignature, .noData, .senderBlocked, .selfSend, .decryptionFailed: return false default: return true } } @@ -30,15 +33,18 @@ public enum MessageReceiver { case .invalidMessage: return "Invalid message." case .unknownMessage: return "Unknown message type." case .unknownEnvelopeType: return "Unknown envelope type." - case .noUserPublicKey: return "Couldn't find user key pair." + case .noUserX25519KeyPair: return "Couldn't find user X25519 key pair." + case .noUserED25519KeyPair: return "Couldn't find user ED25519 key pair." + case .invalidSignature: return "Invalid message signature." case .noData: return "Received an empty envelope." case .senderBlocked: return "Received a message from a blocked user." case .noThread: return "Couldn't find thread for message." + case .selfSend: return "Message addressed at self." + case .decryptionFailed: return "Decryption failed." // Shared sender keys case .invalidGroupPublicKey: return "Invalid group public key." case .noGroupPrivateKey: return "Missing group private key." case .sharedSecretGenerationFailed: return "Couldn't generate a shared secret." - case .selfSend: return "Message addressed at self." } } } @@ -59,9 +65,20 @@ public enum MessageReceiver { (plaintext, sender) = (envelope.content!, envelope.source!) } else { switch envelope.type { - case .unidentifiedSender: (plaintext, sender) = try decryptWithSignalProtocol(envelope: envelope, using: transaction) + case .unidentifiedSender: + do { + (plaintext, sender) = try decryptWithSessionProtocol(envelope: envelope) + } catch { + // Migration + (plaintext, sender) = try decryptWithSignalProtocol(envelope: envelope, using: transaction) + } case .closedGroupCiphertext: - (plaintext, sender) = try decryptWithSharedSenderKeys(envelope: envelope, using: transaction) + do { + (plaintext, sender) = try decryptWithSessionProtocol(envelope: envelope) + } catch { + // Migration + (plaintext, sender) = try decryptWithSharedSenderKeys(envelope: envelope, using: transaction) + } groupPublicKey = envelope.source default: throw Error.unknownEnvelopeType } diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender+Encryption.swift b/SessionMessagingKit/Sending & Receiving/MessageSender+Encryption.swift index ce582d24a..7138fa2e6 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageSender+Encryption.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageSender+Encryption.swift @@ -1,5 +1,6 @@ import SessionProtocolKit import SessionUtilitiesKit +import Sodium internal extension MessageSender { @@ -11,12 +12,25 @@ internal extension MessageSender { return try cipher.throwswrapped_encryptMessage(recipientPublicKey: publicKey, deviceID: 1, paddedPlaintext: (plaintext as NSData).paddedMessageBody(), senderCertificate: certificate, protocolContext: transaction, useFallbackSessionCipher: true) } + + static func encryptWithSessionProtocol(_ plaintext: Data, for recipientHexEncodedX25519PublicKey: String) throws -> Data { + guard let userED25519KeyPair = SNMessagingKitConfiguration.shared.storage.getUserED25519KeyPair() else { throw Error.noUserED25519KeyPair } + let recipientX25519PublicKey = Data(hex: recipientHexEncodedX25519PublicKey.removing05PrefixIfNeeded()) + let sodium = Sodium() + + let data = plaintext + Data(userED25519KeyPair.publicKey) + recipientX25519PublicKey + guard let signature = sodium.sign.signature(message: Bytes(data), secretKey: userED25519KeyPair.secretKey) else { throw Error.signingFailed } + guard let ciphertext = sodium.box.seal(message: Bytes(plaintext + Data(userED25519KeyPair.publicKey) + + Data(signature)), recipientPublicKey: Bytes(recipientX25519PublicKey)) else { throw Error.encryptionFailed } + + return Data(ciphertext) + } static func encryptWithSharedSenderKeys(_ plaintext: Data, for groupPublicKey: String, using transaction: Any) throws -> Data { // 1. ) Encrypt the data with the user's sender key guard let userPublicKey = SNMessagingKitConfiguration.shared.storage.getUserPublicKey() else { SNLog("Couldn't find user key pair.") - throw Error.noUserPublicKey + throw Error.noUserX25519KeyPair } let (ivAndCiphertext, keyIndex) = try SharedSenderKeys.encrypt((plaintext as NSData).paddedMessageBody(), for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction) let encryptedMessage = ClosedGroupCiphertextMessage(_throws_withIVAndCiphertext: ivAndCiphertext, senderPublicKey: Data(hex: userPublicKey), keyIndex: UInt32(keyIndex)) diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender.swift b/SessionMessagingKit/Sending & Receiving/MessageSender.swift index 30aa77af3..998d7693c 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageSender.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageSender.swift @@ -10,7 +10,10 @@ public final class MessageSender : NSObject { case invalidMessage case protoConversionFailed case proofOfWorkCalculationFailed - case noUserPublicKey + case noUserX25519KeyPair + case noUserED25519KeyPair + case signingFailed + case encryptionFailed // Closed groups case noThread case noPrivateKey @@ -18,7 +21,7 @@ public final class MessageSender : NSObject { internal var isRetryable: Bool { switch self { - case .invalidMessage, .protoConversionFailed, .proofOfWorkCalculationFailed, .invalidClosedGroupUpdate: return false + case .invalidMessage, .protoConversionFailed, .proofOfWorkCalculationFailed, .invalidClosedGroupUpdate, .signingFailed, .encryptionFailed: return false default: return true } } @@ -28,7 +31,10 @@ public final class MessageSender : NSObject { case .invalidMessage: return "Invalid message." case .protoConversionFailed: return "Couldn't convert message to proto." case .proofOfWorkCalculationFailed: return "Proof of work calculation failed." - case .noUserPublicKey: return "Couldn't find user key pair." + case .noUserX25519KeyPair: return "Couldn't find user X25519 key pair." + case .noUserED25519KeyPair: return "Couldn't find user ED25519 key pair." + case .signingFailed: return "Couldn't sign message." + case .encryptionFailed: return "Couldn't encrypt message." // Closed groups case .noThread: return "Couldn't find a thread associated with the given group public key." case .noPrivateKey: return "Couldn't find a private key associated with the given group public key." diff --git a/SessionMessagingKit/Storage.swift b/SessionMessagingKit/Storage.swift index 0a0ad7b2d..590f66092 100644 --- a/SessionMessagingKit/Storage.swift +++ b/SessionMessagingKit/Storage.swift @@ -1,5 +1,6 @@ import SessionProtocolKit import PromiseKit +import Sodium public protocol SessionMessagingKitStorageProtocol { @@ -15,6 +16,7 @@ public protocol SessionMessagingKitStorageProtocol { func getUserPublicKey() -> String? func getUserKeyPair() -> ECKeyPair? + func getUserED25519KeyPair() -> Box.KeyPair? func getUserDisplayName() -> String? func getUserProfileKey() -> Data? func getUserProfilePictureURL() -> String? diff --git a/Session/Utilities/Sodium+Conversion.swift b/SessionMessagingKit/Utilities/Sodium+Conversion.swift similarity index 100% rename from Session/Utilities/Sodium+Conversion.swift rename to SessionMessagingKit/Utilities/Sodium+Conversion.swift diff --git a/SessionUtilitiesKit/String+SSK.swift b/SessionUtilitiesKit/String+SSK.swift index 3d13c9180..ae50abb28 100644 --- a/SessionUtilitiesKit/String+SSK.swift +++ b/SessionUtilitiesKit/String+SSK.swift @@ -5,6 +5,7 @@ import Foundation public extension String { + var digitsOnly: String { return (self as NSString).digitsOnly() } diff --git a/SessionUtilitiesKit/UIView+OWS.m b/SessionUtilitiesKit/UIView+OWS.m index d4d8ae493..aca47ba45 100644 --- a/SessionUtilitiesKit/UIView+OWS.m +++ b/SessionUtilitiesKit/UIView+OWS.m @@ -500,7 +500,6 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value) - (UIView *)addBorderViewWithColor:(UIColor *)color strokeWidth:(CGFloat)strokeWidth cornerRadius:(CGFloat)cornerRadius { - UIView *borderView = [UIView new]; borderView.userInteractionEnabled = NO; borderView.backgroundColor = UIColor.clearColor; diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 7b2d9ad8c..dc7d26319 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -244,6 +244,7 @@ B8566C63256F55930045A0B9 /* OWSLinkPreview+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8566C62256F55930045A0B9 /* OWSLinkPreview+Conversion.swift */; }; B8566C6C256F60F50045A0B9 /* OWSUserProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2D1255B6DAF007E1867 /* OWSUserProfile.m */; }; B8566C7D256F62030045A0B9 /* OWSUserProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2D3255B6DAF007E1867 /* OWSUserProfile.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B866CE112581C1A900535CC4 /* Sodium+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E7134E251C867C009649BB /* Sodium+Conversion.swift */; }; B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; }; B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; }; B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */; }; @@ -914,7 +915,6 @@ C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */; }; C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */; }; C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */; }; - C3E7134F251C867C009649BB /* Sodium+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E7134E251C867C009649BB /* Sodium+Conversion.swift */; }; C3ECBF7B257056B700EA7FCE /* Threading.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3ECBF7A257056B700EA7FCE /* Threading.swift */; }; C3F0A530255C80BC007BE2A3 /* NoopNotificationsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F0A52F255C80BC007BE2A3 /* NoopNotificationsManager.swift */; }; D2179CFC16BB0B3A0006F3AB /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2179CFB16BB0B3A0006F3AB /* CoreTelephony.framework */; }; @@ -2603,7 +2603,6 @@ C31FFE56254A5FFE00F19441 /* KeyPairUtilities.swift */, B84664F4235022F30083A1CD /* MentionUtilities.swift */, B886B4A82398BA1500211ABE /* QRCode.swift */, - C3E7134E251C867C009649BB /* Sodium+Conversion.swift */, B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */, B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */, C31A6C59247F214E001123EF /* UIView+Glow.swift */, @@ -3372,6 +3371,7 @@ C33FDB91255A581200E217F9 /* ProtoUtils.h */, C33FDA6C255A57FA00E217F9 /* ProtoUtils.m */, C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */, + C3E7134E251C867C009649BB /* Sodium+Conversion.swift */, C33FDB31255A580A00E217F9 /* SSKEnvironment.h */, C33FDAF4255A580600E217F9 /* SSKEnvironment.m */, C33FDB32255A580A00E217F9 /* SSKIncrementingIdFinder.swift */, @@ -5253,6 +5253,7 @@ C3D9E3BE25676AD70040E4F3 /* TSAttachmentPointer.m in Sources */, C3ECBF7B257056B700EA7FCE /* Threading.swift in Sources */, C32C5AAB256DBE8F003C73A2 /* TSIncomingMessage+Conversion.swift in Sources */, + B866CE112581C1A900535CC4 /* Sodium+Conversion.swift in Sources */, C32C5A88256DBCF9003C73A2 /* MessageReceiver+Handling.swift in Sources */, C32C5C1B256DC9E0003C73A2 /* General.swift in Sources */, C32C5A02256DB658003C73A2 /* MessageSender+Convenience.swift in Sources */, @@ -5522,7 +5523,6 @@ 34D1F0831F8678AA0066283D /* ConversationInputTextView.m in Sources */, 340FC8B6204DAC8D007AEB0F /* OWSQRCodeScanningViewController.m in Sources */, 4CB5F26920F7D060004D1B42 /* MessageActions.swift in Sources */, - C3E7134F251C867C009649BB /* Sodium+Conversion.swift in Sources */, 340FC8B5204DAC8D007AEB0F /* AboutTableViewController.m in Sources */, C33100082558FF6D00070591 /* NewConversationButtonSet.swift in Sources */, B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */, From a46e9e3eb5b3f4a28e267c54634d54bfab60dfbd Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 14 Dec 2020 10:20:10 +1100 Subject: [PATCH 2/9] Recommend that users migrate --- Session/View Controllers/HomeVC.swift | 14 ++++++ .../KeyPairMigrationSheet.swift | 48 +++++++++++++++++++ SessionUtilitiesKit/LKUserDefaults.swift | 1 + Signal.xcodeproj/project.pbxproj | 4 ++ 4 files changed, 67 insertions(+) create mode 100644 Session/View Controllers/KeyPairMigrationSheet.swift diff --git a/Session/View Controllers/HomeVC.swift b/Session/View Controllers/HomeVC.swift index 940766973..8664d5f16 100644 --- a/Session/View Controllers/HomeVC.swift +++ b/Session/View Controllers/HomeVC.swift @@ -161,6 +161,20 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol super.viewDidAppear(animated) isViewVisible = true UserDefaults.standard[.hasLaunchedOnce] = true + showKeyPairMigrationNudgeIfNeeded() + } + + private func showKeyPairMigrationNudgeIfNeeded() { + guard !KeyPairUtilities.hasV2KeyPair() else { return } + let lastNudge = UserDefaults.standard[.lastKeyPairMigrationNudge] + let nudgeInterval: Double = 3 * 24 * 60 * 60 // 3 days + let nudge = given(lastNudge) { Date().timeIntervalSince($0) > nudgeInterval } ?? true + guard nudge else { return } + let sheet = KeyPairMigrationSheet() + sheet.modalPresentationStyle = .overFullScreen + sheet.modalTransitionStyle = .crossDissolve + present(sheet, animated: true, completion: nil) + UserDefaults.standard[.lastKeyPairMigrationNudge] = Date() } override func viewWillDisappear(_ animated: Bool) { diff --git a/Session/View Controllers/KeyPairMigrationSheet.swift b/Session/View Controllers/KeyPairMigrationSheet.swift new file mode 100644 index 000000000..154ae8c19 --- /dev/null +++ b/Session/View Controllers/KeyPairMigrationSheet.swift @@ -0,0 +1,48 @@ + +final class KeyPairMigrationSheet : Sheet { + + override func populateContentView() { + // Set up image view + let imageView = UIImageView(image: #imageLiteral(resourceName: "Sun")) + // Set up title label + let titleLabel = UILabel() + titleLabel.textColor = Colors.text + titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize) + titleLabel.text = "Session IDs Just Got Better" + titleLabel.numberOfLines = 0 + titleLabel.lineBreakMode = .byWordWrapping + // Set up top stack view + let topStackView = UIStackView(arrangedSubviews: [ imageView, titleLabel ]) + topStackView.axis = .vertical + topStackView.spacing = Values.largeSpacing + topStackView.alignment = .center + // Set up explanation label + let explanationLabel = UILabel() + explanationLabel.textColor = Colors.text + explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) + explanationLabel.textAlignment = .center + explanationLabel.text = """ + We upgraded the way Session IDs work. They're now even more secure bla bla bla. + + Migrate now to ensure your account is as secure as possible. + """ + explanationLabel.numberOfLines = 0 + explanationLabel.lineBreakMode = .byWordWrapping + // Set up OK button + let okButton = Button(style: .prominentOutline, size: .large) + okButton.set(.width, to: 240) + okButton.setTitle(NSLocalizedString("OK", comment: ""), for: UIControl.State.normal) + okButton.addTarget(self, action: #selector(close), for: UIControl.Event.touchUpInside) + // Set up main stack view + let stackView = UIStackView(arrangedSubviews: [ topStackView, explanationLabel, okButton ]) + stackView.axis = .vertical + stackView.spacing = Values.veryLargeSpacing + stackView.alignment = .center + // Set up constraints + contentView.addSubview(stackView) + stackView.pin(.leading, to: .leading, of: contentView, withInset: Values.veryLargeSpacing) + stackView.pin(.top, to: .top, of: contentView, withInset: Values.largeSpacing) + contentView.pin(.trailing, to: .trailing, of: stackView, withInset: Values.veryLargeSpacing) + contentView.pin(.bottom, to: .bottom, of: stackView, withInset: Values.veryLargeSpacing + overshoot) + } +} diff --git a/SessionUtilitiesKit/LKUserDefaults.swift b/SessionUtilitiesKit/LKUserDefaults.swift index 077c7c2a9..1286814ab 100644 --- a/SessionUtilitiesKit/LKUserDefaults.swift +++ b/SessionUtilitiesKit/LKUserDefaults.swift @@ -11,6 +11,7 @@ public enum LKUserDefaults { public enum Date : Swift.String { case lastProfilePictureUpload + case lastKeyPairMigrationNudge } public enum Double : Swift.String { diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index dc7d26319..d9f81cbd3 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -236,6 +236,7 @@ B82B408E239DC00D00A248E7 /* DisplayNameVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B408D239DC00D00A248E7 /* DisplayNameVC.swift */; }; B82B4090239DD75000A248E7 /* RestoreVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B408F239DD75000A248E7 /* RestoreVC.swift */; }; B82B4094239DF15900A248E7 /* ConversationTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B4093239DF15900A248E7 /* ConversationTitleView.swift */; }; + B83786802586D296003CE78E /* KeyPairMigrationSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B837867F2586D296003CE78E /* KeyPairMigrationSheet.swift */; }; B83F2B88240CB75A000A54AB /* UIImage+Scaling.swift in Sources */ = {isa = PBXBuildFile; fileRef = B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */; }; B84664F5235022F30083A1CD /* MentionUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84664F4235022F30083A1CD /* MentionUtilities.swift */; }; B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */; }; @@ -1362,6 +1363,7 @@ B82B408D239DC00D00A248E7 /* DisplayNameVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayNameVC.swift; sourceTree = ""; }; B82B408F239DD75000A248E7 /* RestoreVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestoreVC.swift; sourceTree = ""; }; B82B4093239DF15900A248E7 /* ConversationTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationTitleView.swift; sourceTree = ""; }; + B837867F2586D296003CE78E /* KeyPairMigrationSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPairMigrationSheet.swift; sourceTree = ""; }; B83F2B85240C7B8F000A54AB /* NewConversationButtonSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationButtonSet.swift; sourceTree = ""; }; B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Scaling.swift"; sourceTree = ""; }; B84072952565E9F50037CB17 /* TSOutgoingMessage+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSOutgoingMessage+Conversion.swift"; sourceTree = ""; }; @@ -2620,6 +2622,7 @@ B88847BB23E10BC6009836D2 /* GroupMembersVC.swift */, B8BB82A4238F627000BA5194 /* HomeVC.swift */, B8CCF63E23975CFB0091D419 /* JoinPublicChatVC.swift */, + B837867F2586D296003CE78E /* KeyPairMigrationSheet.swift */, B82B40872399EB0E00A248E7 /* LandingVC.swift */, C329FEEB24F7277900B1C64C /* LightModeSheet.swift */, B86BD08323399ACF000F5AE3 /* Modal.swift */, @@ -5532,6 +5535,7 @@ 340FC8AC204DAC8D007AEB0F /* PrivacySettingsTableViewController.m in Sources */, B88847BC23E10BC6009836D2 /* GroupMembersVC.swift in Sources */, B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */, + B83786802586D296003CE78E /* KeyPairMigrationSheet.swift in Sources */, C35E8AAE2485E51D00ACB629 /* IP2Country.swift in Sources */, 340FC8AE204DAC8D007AEB0F /* OWSSoundSettingsViewController.m in Sources */, 340FC8B0204DAC8D007AEB0F /* AddToBlockListViewController.m in Sources */, From 6df2878d21d46d8a657e153f0084805a53a6cd55 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 14 Dec 2020 14:30:34 +1100 Subject: [PATCH 3/9] Implement actual prompt design --- .../KeyPairMigrationSheet.swift | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/Session/View Controllers/KeyPairMigrationSheet.swift b/Session/View Controllers/KeyPairMigrationSheet.swift index 154ae8c19..9240d81c4 100644 --- a/Session/View Controllers/KeyPairMigrationSheet.swift +++ b/Session/View Controllers/KeyPairMigrationSheet.swift @@ -2,47 +2,70 @@ final class KeyPairMigrationSheet : Sheet { override func populateContentView() { - // Set up image view + // Image view let imageView = UIImageView(image: #imageLiteral(resourceName: "Sun")) - // Set up title label + // Title label let titleLabel = UILabel() titleLabel.textColor = Colors.text titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize) titleLabel.text = "Session IDs Just Got Better" titleLabel.numberOfLines = 0 titleLabel.lineBreakMode = .byWordWrapping - // Set up top stack view + // Top stack view let topStackView = UIStackView(arrangedSubviews: [ imageView, titleLabel ]) topStackView.axis = .vertical topStackView.spacing = Values.largeSpacing topStackView.alignment = .center - // Set up explanation label + // Explanation label let explanationLabel = UILabel() explanationLabel.textColor = Colors.text explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.textAlignment = .center explanationLabel.text = """ - We upgraded the way Session IDs work. They're now even more secure bla bla bla. + We’ve upgraded Session IDs to make them even more private and secure. We recommend upgrading to a new Session ID now. - Migrate now to ensure your account is as secure as possible. + You will lose existing contacts and conversations, but you’ll gain even more privacy and security. You will need to upgrade your Session ID eventually, but you can choose to delay the upgrade if you need to save contacts or conversations. """ explanationLabel.numberOfLines = 0 explanationLabel.lineBreakMode = .byWordWrapping - // Set up OK button - let okButton = Button(style: .prominentOutline, size: .large) - okButton.set(.width, to: 240) - okButton.setTitle(NSLocalizedString("OK", comment: ""), for: UIControl.State.normal) - okButton.addTarget(self, action: #selector(close), for: UIControl.Event.touchUpInside) - // Set up main stack view - let stackView = UIStackView(arrangedSubviews: [ topStackView, explanationLabel, okButton ]) + // Upgrade now button + let upgradeNowButton = Button(style: .prominentOutline, size: .large) + upgradeNowButton.set(.width, to: 240) + upgradeNowButton.setTitle(NSLocalizedString("Upgrade Now", comment: ""), for: UIControl.State.normal) + upgradeNowButton.addTarget(self, action: #selector(upgradeNow), for: UIControl.Event.touchUpInside) + // Upgrade later button + let upgradeLaterButton = Button(style: .prominentOutline, size: .large) + upgradeLaterButton.set(.width, to: 240) + upgradeLaterButton.setTitle(NSLocalizedString("Upgrade Later", comment: ""), for: UIControl.State.normal) + upgradeLaterButton.addTarget(self, action: #selector(close), for: UIControl.Event.touchUpInside) + // Button stack view + let buttonStackView = UIStackView(arrangedSubviews: [ upgradeNowButton, upgradeLaterButton ]) + buttonStackView.axis = .vertical + buttonStackView.spacing = Values.mediumSpacing + buttonStackView.alignment = .center + // Main stack view + let stackView = UIStackView(arrangedSubviews: [ topStackView, explanationLabel, buttonStackView ]) stackView.axis = .vertical stackView.spacing = Values.veryLargeSpacing stackView.alignment = .center - // Set up constraints + // Constraints contentView.addSubview(stackView) stackView.pin(.leading, to: .leading, of: contentView, withInset: Values.veryLargeSpacing) stackView.pin(.top, to: .top, of: contentView, withInset: Values.largeSpacing) contentView.pin(.trailing, to: .trailing, of: stackView, withInset: Values.veryLargeSpacing) contentView.pin(.bottom, to: .bottom, of: stackView, withInset: Values.veryLargeSpacing + overshoot) } + + @objc private func upgradeNow() { + guard let presentingVC = presentingViewController else { return } + let message = "You’re upgrading to a new Session ID. This will give you improved privacy and security, but it will clear ALL app data. Contacts and conversations will be lost. Proceed?" + let alert = UIAlertController(title: "Upgrade Session ID?", message: message, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "Yes", style: .destructive) { _ in + NotificationCenter.default.post(name: .dataNukeRequested, object: nil) + }) + alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil)) + presentingVC.dismiss(animated: true) { // Dismiss self first + presentingVC.present(alert, animated: true, completion: nil) + } + } } From 0864873d317759a0c1f042740a55fc3fa1100ffd Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 14 Dec 2020 14:43:17 +1100 Subject: [PATCH 4/9] Clean --- Session/Signal/AppDelegate.m | 4 ++-- Session/Utilities/Storage+Resetting.swift | 23 +++++++++++++++++++ Session/View Controllers/HomeVC.swift | 4 ++-- .../KeyPairMigrationSheet.swift | 2 +- .../Notifications/PushNotificationAPI.swift | 10 ++++---- SessionMessagingKit/To Do/TSAccountManager.m | 2 +- Signal.xcodeproj/project.pbxproj | 4 ++++ 7 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 Session/Utilities/Storage+Resetting.swift diff --git a/Session/Signal/AppDelegate.m b/Session/Signal/AppDelegate.m index f02985166..ed8040a59 100644 --- a/Session/Signal/AppDelegate.m +++ b/Session/Signal/AppDelegate.m @@ -633,7 +633,7 @@ static NSTimeInterval launchStartedAt; if (isUsingFullAPNs) { __unused AnyPromise *promise = [LKPushNotificationAPI registerWithToken:deviceToken hexEncodedPublicKey:self.tsAccountManager.localNumber isForcedUpdate:NO]; } else { - __unused AnyPromise *promise = [LKPushNotificationAPI unregisterWithToken:deviceToken isForcedUpdate:NO]; + __unused AnyPromise *promise = [LKPushNotificationAPI unregisterToken:deviceToken]; } } @@ -817,7 +817,7 @@ static NSTimeInterval launchStartedAt; NSString *hexEncodedDeviceToken = [userDefaults stringForKey:@"deviceToken"]; if (isUsingFullAPNs && hexEncodedDeviceToken != nil) { NSData *deviceToken = [NSData dataFromHexString:hexEncodedDeviceToken]; - [[LKPushNotificationAPI unregisterWithToken:deviceToken isForcedUpdate:YES] retainUntilComplete]; + [[LKPushNotificationAPI unregisterToken:deviceToken] retainUntilComplete]; } [ThreadUtil deleteAllContent]; [SSKEnvironment.shared.identityManager clearIdentityKey]; diff --git a/Session/Utilities/Storage+Resetting.swift b/Session/Utilities/Storage+Resetting.swift new file mode 100644 index 000000000..ed132c393 --- /dev/null +++ b/Session/Utilities/Storage+Resetting.swift @@ -0,0 +1,23 @@ + +extension Storage { + + static func reset() { + let userDefaults = UserDefaults.standard + if userDefaults[.isUsingFullAPNs], let hexEncodedToken = userDefaults[.deviceToken] { + let token = Data(hex: hexEncodedToken) + PushNotificationAPI.unregister(token).retainUntilComplete() // TODO: Wait for this to complete? + } + + let appDelegate = UIApplication.shared.delegate as! AppDelegate + appDelegate.stopPoller() + appDelegate.stopClosedGroupPoller() + appDelegate.stopOpenGroupPollers() + + OWSStorage.resetAllStorage() + OWSUserProfile.resetProfileStorage() + Environment.shared.preferences.clear() + AppEnvironment.shared.notificationPresenter.clearAllNotifications() + + exit(0) + } +} diff --git a/Session/View Controllers/HomeVC.swift b/Session/View Controllers/HomeVC.swift index 8664d5f16..f342aa496 100644 --- a/Session/View Controllers/HomeVC.swift +++ b/Session/View Controllers/HomeVC.swift @@ -165,11 +165,11 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol } private func showKeyPairMigrationNudgeIfNeeded() { - guard !KeyPairUtilities.hasV2KeyPair() else { return } +// guard !KeyPairUtilities.hasV2KeyPair() else { return } let lastNudge = UserDefaults.standard[.lastKeyPairMigrationNudge] let nudgeInterval: Double = 3 * 24 * 60 * 60 // 3 days let nudge = given(lastNudge) { Date().timeIntervalSince($0) > nudgeInterval } ?? true - guard nudge else { return } +// guard nudge else { return } let sheet = KeyPairMigrationSheet() sheet.modalPresentationStyle = .overFullScreen sheet.modalTransitionStyle = .crossDissolve diff --git a/Session/View Controllers/KeyPairMigrationSheet.swift b/Session/View Controllers/KeyPairMigrationSheet.swift index 9240d81c4..12e2e0b40 100644 --- a/Session/View Controllers/KeyPairMigrationSheet.swift +++ b/Session/View Controllers/KeyPairMigrationSheet.swift @@ -61,7 +61,7 @@ final class KeyPairMigrationSheet : Sheet { let message = "You’re upgrading to a new Session ID. This will give you improved privacy and security, but it will clear ALL app data. Contacts and conversations will be lost. Proceed?" let alert = UIAlertController(title: "Upgrade Session ID?", message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Yes", style: .destructive) { _ in - NotificationCenter.default.post(name: .dataNukeRequested, object: nil) + Storage.reset() }) alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil)) presentingVC.dismiss(animated: true) { // Dismiss self first diff --git a/SessionMessagingKit/Sending & Receiving/Notifications/PushNotificationAPI.swift b/SessionMessagingKit/Sending & Receiving/Notifications/PushNotificationAPI.swift index a7076ad8c..e4783fc94 100644 --- a/SessionMessagingKit/Sending & Receiving/Notifications/PushNotificationAPI.swift +++ b/SessionMessagingKit/Sending & Receiving/Notifications/PushNotificationAPI.swift @@ -19,7 +19,7 @@ public final class PushNotificationAPI : NSObject { private override init() { } // MARK: Registration - static func unregister(with token: Data, isForcedUpdate: Bool) -> Promise { + public static func unregister(_ token: Data) -> Promise { let hexEncodedToken = token.toHexString() let parameters = [ "token" : hexEncodedToken ] let url = URL(string: "\(server)/unregister")! @@ -45,12 +45,12 @@ public final class PushNotificationAPI : NSObject { return promise } - @objc(unregisterWithToken:isForcedUpdate:) - public static func objc_unregister(with token: Data, isForcedUpdate: Bool) -> AnyPromise { - return AnyPromise.from(unregister(with: token, isForcedUpdate: isForcedUpdate)) + @objc(unregisterToken:) + public static func objc_unregister(token: Data) -> AnyPromise { + return AnyPromise.from(unregister(token)) } - static func register(with token: Data, publicKey: String, isForcedUpdate: Bool) -> Promise { + public static func register(with token: Data, publicKey: String, isForcedUpdate: Bool) -> Promise { let hexEncodedToken = token.toHexString() let userDefaults = UserDefaults.standard let oldToken = userDefaults[.deviceToken] diff --git a/SessionMessagingKit/To Do/TSAccountManager.m b/SessionMessagingKit/To Do/TSAccountManager.m index 2ee6e4b8c..03c1cfcc4 100644 --- a/SessionMessagingKit/To Do/TSAccountManager.m +++ b/SessionMessagingKit/To Do/TSAccountManager.m @@ -277,7 +277,7 @@ NSString *const TSAccountManager_NeedsAccountAttributesUpdateKey = @"TSAccountMa BOOL isUsingFullAPNs = [NSUserDefaults.standardUserDefaults boolForKey:@"isUsingFullAPNs"]; NSData *pushTokenAsData = [NSData dataFromHexString:pushToken]; AnyPromise *promise = isUsingFullAPNs ? [LKPushNotificationAPI registerWithToken:pushTokenAsData hexEncodedPublicKey:self.localNumber isForcedUpdate:isForcedUpdate] - : [LKPushNotificationAPI unregisterWithToken:pushTokenAsData isForcedUpdate:isForcedUpdate]; + : [LKPushNotificationAPI unregisterToken:pushTokenAsData]; promise .then(^() { successHandler(); diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index d9f81cbd3..f038f3459 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -245,6 +245,7 @@ B8566C63256F55930045A0B9 /* OWSLinkPreview+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8566C62256F55930045A0B9 /* OWSLinkPreview+Conversion.swift */; }; B8566C6C256F60F50045A0B9 /* OWSUserProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2D1255B6DAF007E1867 /* OWSUserProfile.m */; }; B8566C7D256F62030045A0B9 /* OWSUserProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2D3255B6DAF007E1867 /* OWSUserProfile.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B85A68B12587141A008CC492 /* Storage+Resetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85A68B02587141A008CC492 /* Storage+Resetting.swift */; }; B866CE112581C1A900535CC4 /* Sodium+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E7134E251C867C009649BB /* Sodium+Conversion.swift */; }; B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; }; B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; }; @@ -1375,6 +1376,7 @@ B8544E3023D16CA500299F14 /* DeviceUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceUtilities.swift; sourceTree = ""; }; B8544E3223D50E4900299F14 /* AppearanceUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceUtilities.swift; sourceTree = ""; }; B8566C62256F55930045A0B9 /* OWSLinkPreview+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OWSLinkPreview+Conversion.swift"; sourceTree = ""; }; + B85A68B02587141A008CC492 /* Storage+Resetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Resetting.swift"; sourceTree = ""; }; B86BD08323399ACF000F5AE3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = ""; }; B86BD08523399CEF000F5AE3 /* SeedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedModal.swift; sourceTree = ""; }; B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Interaction.swift"; sourceTree = ""; }; @@ -2605,6 +2607,7 @@ C31FFE56254A5FFE00F19441 /* KeyPairUtilities.swift */, B84664F4235022F30083A1CD /* MentionUtilities.swift */, B886B4A82398BA1500211ABE /* QRCode.swift */, + B85A68B02587141A008CC492 /* Storage+Resetting.swift */, B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */, B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */, C31A6C59247F214E001123EF /* UIView+Glow.swift */, @@ -5432,6 +5435,7 @@ C396DAF42518408B00FF6DC5 /* Parser.swift in Sources */, 4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */, 4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */, + B85A68B12587141A008CC492 /* Storage+Resetting.swift in Sources */, 3496955E219B605E00DCFE74 /* PhotoLibrary.swift in Sources */, 45D231771DC7E8F10034FA89 /* SessionResetJob.swift in Sources */, 340FC8A9204DAC8D007AEB0F /* NotificationSettingsOptionsViewController.m in Sources */, From f7919b8d4f33277a9758da24090e4c0b1076e655 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 14 Dec 2020 15:20:25 +1100 Subject: [PATCH 5/9] Undo accidental commit --- Session/View Controllers/HomeVC.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Session/View Controllers/HomeVC.swift b/Session/View Controllers/HomeVC.swift index f342aa496..8664d5f16 100644 --- a/Session/View Controllers/HomeVC.swift +++ b/Session/View Controllers/HomeVC.swift @@ -165,11 +165,11 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol } private func showKeyPairMigrationNudgeIfNeeded() { -// guard !KeyPairUtilities.hasV2KeyPair() else { return } + guard !KeyPairUtilities.hasV2KeyPair() else { return } let lastNudge = UserDefaults.standard[.lastKeyPairMigrationNudge] let nudgeInterval: Double = 3 * 24 * 60 * 60 // 3 days let nudge = given(lastNudge) { Date().timeIntervalSince($0) > nudgeInterval } ?? true -// guard nudge else { return } + guard nudge else { return } let sheet = KeyPairMigrationSheet() sheet.modalPresentationStyle = .overFullScreen sheet.modalTransitionStyle = .crossDissolve From fa02ea11de17a3d0306142403d3dbf04d5a45d25 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 14 Dec 2020 16:09:11 +1100 Subject: [PATCH 6/9] Show message sending status bar earlier --- .../Sending & Receiving/MessageSender+Convenience.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/SignalUtilitiesKit/Messaging/Sending & Receiving/MessageSender+Convenience.swift b/SignalUtilitiesKit/Messaging/Sending & Receiving/MessageSender+Convenience.swift index bb7279306..ee7742dd9 100644 --- a/SignalUtilitiesKit/Messaging/Sending & Receiving/MessageSender+Convenience.swift +++ b/SignalUtilitiesKit/Messaging/Sending & Receiving/MessageSender+Convenience.swift @@ -3,6 +3,7 @@ import PromiseKit extension MessageSender { + // MARK: Durable @objc(send:withAttachments:inThread:usingTransaction:) public static func send(_ message: VisibleMessage, with attachments: [SignalAttachment], in thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) { prep(attachments, for: message, using: transaction) @@ -15,8 +16,16 @@ extension MessageSender { let destination = Message.Destination.from(thread) let job = MessageSendJob(message: message, destination: destination) JobQueue.shared.add(job, using: transaction) + guard let userPublicKey = SNMessagingKitConfiguration.shared.storage.getUserPublicKey() else { return } + if case .contact(let recipientPublicKey) = destination, message is VisibleMessage, recipientPublicKey != userPublicKey { + DispatchQueue.main.async { + // Not strictly true, but nicer from a UX perspective + NotificationCenter.default.post(name: .encryptingMessage, object: NSNumber(value: message.sentTimestamp!)) + } + } } + // MARK: Non-Durable @objc(sendNonDurably:withAttachments:inThread:usingTransaction:) public static func objc_sendNonDurably(_ message: VisibleMessage, with attachments: [SignalAttachment], in thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) -> AnyPromise { return AnyPromise.from(sendNonDurably(message, with: attachments, in: thread, using: transaction)) From a15eec83eb0478541ddda7550528fe7a352f8076 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Wed, 16 Dec 2020 11:45:01 +1100 Subject: [PATCH 7/9] Minor refactoring --- .../Sending & Receiving/MessageReceiver+Decryption.swift | 3 ++- .../Sending & Receiving/MessageSender+Encryption.swift | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift index ea502640b..a20313452 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift @@ -45,7 +45,8 @@ internal extension MessageReceiver { let senderED25519PublicKey = Bytes(plaintextWithMetadata[plaintextWithMetadata.count - (signatureSize + ed25519PublicKeySize) ..< plaintextWithMetadata.count - signatureSize]) let plaintext = Bytes(plaintextWithMetadata[0.. Date: Wed, 16 Dec 2020 14:22:31 +1100 Subject: [PATCH 8/9] Use actual icon --- .../Loki V2/Shield.imageset/Contents.json | 12 ++++++++++++ .../Loki V2/Shield.imageset/Shield.pdf | Bin 0 -> 1275 bytes .../View Controllers/KeyPairMigrationSheet.swift | 5 ++++- 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 Session/Meta/Images.xcassets/Loki V2/Shield.imageset/Contents.json create mode 100644 Session/Meta/Images.xcassets/Loki V2/Shield.imageset/Shield.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/Shield.imageset/Contents.json b/Session/Meta/Images.xcassets/Loki V2/Shield.imageset/Contents.json new file mode 100644 index 000000000..c6de4ea4e --- /dev/null +++ b/Session/Meta/Images.xcassets/Loki V2/Shield.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Shield.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Session/Meta/Images.xcassets/Loki V2/Shield.imageset/Shield.pdf b/Session/Meta/Images.xcassets/Loki V2/Shield.imageset/Shield.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f03001d0aa8dd011d8dff16a5b64ec9c474e843f GIT binary patch literal 1275 zcmah}TWC{B7bRTxeoPXw<|NNJ4 zz7tP09I23en&252z5ZEH5k~g5?Lsh!#P!5VvJRvH6cZ3aV#MrZgo5of7zvZOoghM0 zmEbT+j4mN_wkuk1&6G;+eqOMrt>nz%Y~!{s_w48fk1E`SVEj<>hC-?9GD%Z+2Oe)%}^R2(F3 z%BBbsqehY-50c!tQG*hT!K;OP+CUPfQDbM&W`HnK0;)S(UGI=>F>rSfzv|q*)=@j1 zDk>_M!$k{qHbG`;SWb%Pif0ROJz_LX3cC<^g~={6PGWU69L)71#)xC5X`DESGZHK6 zjMrR{+xN~K{;t?yCLHL1Hy=ZV+S9zzq5yhPfGD*S={N|~8#hd9BgrS}K8(B_jHR5_ zBG3zHk{YQFGw%3onk+AO6NNy&%%&(9sf|Q1Rx2^qYhl)4>{r0@DZv_p)i9R9DzKNq z2aa-<@A*0aJ}~`iegej0DAjNVvH8^hFFwx=etrn3LsGfYRfOo4Y1&qpE65vOEkj00 zmUJj4plU7UMXdOXSjo{EGMLe6C*6Lzc$~akzKbkFNxOhiK!B3-1L>NkXs8_(FkJ~C z_Y7H6m@0$D_$a~@^}l{tf{$dnpA7dm4fA6V$uz7o075BXj2dPqq3&N)li5w6WG Date: Wed, 16 Dec 2020 14:28:14 +1100 Subject: [PATCH 9/9] Fix tint color --- Session/View Controllers/KeyPairMigrationSheet.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Session/View Controllers/KeyPairMigrationSheet.swift b/Session/View Controllers/KeyPairMigrationSheet.swift index 99a7cb4bf..c5ebecaae 100644 --- a/Session/View Controllers/KeyPairMigrationSheet.swift +++ b/Session/View Controllers/KeyPairMigrationSheet.swift @@ -3,7 +3,7 @@ final class KeyPairMigrationSheet : Sheet { override func populateContentView() { // Image view - let imageView = UIImageView(image: #imageLiteral(resourceName: "Shield")) + let imageView = UIImageView(image: #imageLiteral(resourceName: "Shield").withTint(Colors.text)) imageView.set(.width, to: 64) imageView.set(.height, to: 64) imageView.contentMode = .scaleAspectFit