Merge pull request #318 from loki-project/session-protocol
Session Protocol
This commit is contained in:
commit
ad3f04a8f8
1
Podfile
1
Podfile
|
@ -71,6 +71,7 @@ target 'SessionMessagingKit' do
|
||||||
pod 'Reachability', :inhibit_warnings => true
|
pod 'Reachability', :inhibit_warnings => true
|
||||||
pod 'SAMKeychain', :inhibit_warnings => true
|
pod 'SAMKeychain', :inhibit_warnings => true
|
||||||
pod 'SignalCoreKit', git: 'https://github.com/signalapp/SignalCoreKit.git', :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 '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
|
pod 'YapDatabase/SQLCipher', :git => 'https://github.com/loki-project/session-ios-yap-database.git', branch: 'signal-release', :inhibit_warnings => true
|
||||||
end
|
end
|
||||||
|
|
|
@ -230,6 +230,6 @@ SPEC CHECKSUMS:
|
||||||
YYImage: 6db68da66f20d9f169ceb94dfb9947c3867b9665
|
YYImage: 6db68da66f20d9f169ceb94dfb9947c3867b9665
|
||||||
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
|
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
|
||||||
|
|
||||||
PODFILE CHECKSUM: 7699c2a380fc803ef7f51157f1f75da756aa3b45
|
PODFILE CHECKSUM: 3263ab95f60e220882ca53cca4c6bdc2e7a80381
|
||||||
|
|
||||||
COCOAPODS: 1.10.0.rc.1
|
COCOAPODS: 1.10.0.rc.1
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Shield.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
|
@ -633,7 +633,7 @@ static NSTimeInterval launchStartedAt;
|
||||||
if (isUsingFullAPNs) {
|
if (isUsingFullAPNs) {
|
||||||
__unused AnyPromise *promise = [LKPushNotificationAPI registerWithToken:deviceToken hexEncodedPublicKey:self.tsAccountManager.localNumber isForcedUpdate:NO];
|
__unused AnyPromise *promise = [LKPushNotificationAPI registerWithToken:deviceToken hexEncodedPublicKey:self.tsAccountManager.localNumber isForcedUpdate:NO];
|
||||||
} else {
|
} 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"];
|
NSString *hexEncodedDeviceToken = [userDefaults stringForKey:@"deviceToken"];
|
||||||
if (isUsingFullAPNs && hexEncodedDeviceToken != nil) {
|
if (isUsingFullAPNs && hexEncodedDeviceToken != nil) {
|
||||||
NSData *deviceToken = [NSData dataFromHexString:hexEncodedDeviceToken];
|
NSData *deviceToken = [NSData dataFromHexString:hexEncodedDeviceToken];
|
||||||
[[LKPushNotificationAPI unregisterWithToken:deviceToken isForcedUpdate:YES] retainUntilComplete];
|
[[LKPushNotificationAPI unregisterToken:deviceToken] retainUntilComplete];
|
||||||
}
|
}
|
||||||
[ThreadUtil deleteAllContent];
|
[ThreadUtil deleteAllContent];
|
||||||
[SSKEnvironment.shared.identityManager clearIdentityKey];
|
[SSKEnvironment.shared.identityManager clearIdentityKey];
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -161,6 +161,20 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
|
||||||
super.viewDidAppear(animated)
|
super.viewDidAppear(animated)
|
||||||
isViewVisible = true
|
isViewVisible = true
|
||||||
UserDefaults.standard[.hasLaunchedOnce] = 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) {
|
override func viewWillDisappear(_ animated: Bool) {
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
|
||||||
|
final class KeyPairMigrationSheet : Sheet {
|
||||||
|
|
||||||
|
override func populateContentView() {
|
||||||
|
// Image view
|
||||||
|
let imageView = UIImageView(image: #imageLiteral(resourceName: "Shield").withTint(Colors.text))
|
||||||
|
imageView.set(.width, to: 64)
|
||||||
|
imageView.set(.height, to: 64)
|
||||||
|
imageView.contentMode = .scaleAspectFit
|
||||||
|
// 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
|
||||||
|
// Top stack view
|
||||||
|
let topStackView = UIStackView(arrangedSubviews: [ imageView, titleLabel ])
|
||||||
|
topStackView.axis = .vertical
|
||||||
|
topStackView.spacing = Values.largeSpacing
|
||||||
|
topStackView.alignment = .center
|
||||||
|
// Explanation label
|
||||||
|
let explanationLabel = UILabel()
|
||||||
|
explanationLabel.textColor = Colors.text
|
||||||
|
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||||
|
explanationLabel.textAlignment = .center
|
||||||
|
explanationLabel.text = """
|
||||||
|
We’ve upgraded Session IDs to make them even more private and secure. We recommend upgrading to a new Session ID now.
|
||||||
|
|
||||||
|
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
|
||||||
|
// 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
|
||||||
|
// 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
|
||||||
|
Storage.reset()
|
||||||
|
})
|
||||||
|
alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
|
||||||
|
presentingVC.dismiss(animated: true) { // Dismiss self first
|
||||||
|
presentingVC.present(alert, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import PromiseKit
|
import PromiseKit
|
||||||
|
import Sodium
|
||||||
|
|
||||||
extension Storage {
|
extension Storage {
|
||||||
|
|
||||||
|
@ -23,6 +24,16 @@ extension Storage {
|
||||||
public func getUserKeyPair() -> ECKeyPair? {
|
public func getUserKeyPair() -> ECKeyPair? {
|
||||||
return OWSIdentityManager.shared().identityKeyPair()
|
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? {
|
public func getUserDisplayName() -> String? {
|
||||||
return SSKEnvironment.shared.profileManager.localProfileName()
|
return SSKEnvironment.shared.profileManager.localProfileName()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import CryptoSwift
|
import CryptoSwift
|
||||||
import SessionProtocolKit
|
import SessionProtocolKit
|
||||||
import SessionUtilitiesKit
|
import SessionUtilitiesKit
|
||||||
|
import Sodium
|
||||||
|
|
||||||
internal extension MessageReceiver {
|
internal extension MessageReceiver {
|
||||||
|
|
||||||
|
@ -8,7 +9,7 @@ internal extension MessageReceiver {
|
||||||
let storage = SNMessagingKitConfiguration.shared.signalStorage
|
let storage = SNMessagingKitConfiguration.shared.signalStorage
|
||||||
let certificateValidator = SNMessagingKitConfiguration.shared.certificateValidator
|
let certificateValidator = SNMessagingKitConfiguration.shared.certificateValidator
|
||||||
guard let data = envelope.content else { throw Error.noData }
|
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,
|
let cipher = try SMKSecretSessionCipher(sessionResetImplementation: SNMessagingKitConfiguration.shared.sessionRestorationImplementation,
|
||||||
sessionStore: storage, preKeyStore: storage, signedPreKeyStore: storage, identityStore: SNMessagingKitConfiguration.shared.identityKeyStore)
|
sessionStore: storage, preKeyStore: storage, signedPreKeyStore: storage, identityStore: SNMessagingKitConfiguration.shared.identityKeyStore)
|
||||||
let result = try cipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator, cipherTextData: data,
|
let result = try cipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator, cipherTextData: data,
|
||||||
|
@ -16,6 +17,43 @@ internal extension MessageReceiver {
|
||||||
return (result.paddedPayload, result.senderRecipientId)
|
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..<plaintextWithMetadata.count - (signatureSize + ed25519PublicKeySize)])
|
||||||
|
// 3. ) Verify the signature
|
||||||
|
let verificationData = plaintext + senderED25519PublicKey + recipientX25519PublicKey
|
||||||
|
let isValid = sodium.sign.verify(message: verificationData, publicKey: senderED25519PublicKey, signature: signature)
|
||||||
|
guard isValid else { throw Error.invalidSignature }
|
||||||
|
// 4. ) Get the sender's X25519 public key
|
||||||
|
guard let senderX25519PublicKey = sodium.sign.toX25519(ed25519PublicKey: senderED25519PublicKey) else { throw Error.decryptionFailed }
|
||||||
|
|
||||||
|
return (Data(plaintext), "05" + senderX25519PublicKey.toHexString())
|
||||||
|
}
|
||||||
|
|
||||||
static func decryptWithSharedSenderKeys(envelope: SNProtoEnvelope, using transaction: Any) throws -> (plaintext: Data, senderPublicKey: String) {
|
static func decryptWithSharedSenderKeys(envelope: SNProtoEnvelope, using transaction: Any) throws -> (plaintext: Data, senderPublicKey: String) {
|
||||||
// 1. ) Check preconditions
|
// 1. ) Check preconditions
|
||||||
guard let groupPublicKey = envelope.source, SNMessagingKitConfiguration.shared.storage.isClosedGroup(groupPublicKey) else {
|
guard let groupPublicKey = envelope.source, SNMessagingKitConfiguration.shared.storage.isClosedGroup(groupPublicKey) else {
|
||||||
|
@ -36,8 +74,8 @@ internal extension MessageReceiver {
|
||||||
guard let ephemeralSharedSecret = try? Curve25519.generateSharedSecret(fromPublicKey: ephemeralPublicKey, privateKey: groupPrivateKey) else {
|
guard let ephemeralSharedSecret = try? Curve25519.generateSharedSecret(fromPublicKey: ephemeralPublicKey, privateKey: groupPrivateKey) else {
|
||||||
throw Error.sharedSecretGenerationFailed
|
throw Error.sharedSecretGenerationFailed
|
||||||
}
|
}
|
||||||
let salt = "LOKI"
|
let salt = "LOKI".data(using: String.Encoding.utf8, allowLossyConversion: true)!.bytes
|
||||||
let symmetricKey = try HMAC(key: salt.bytes, variant: .sha256).authenticate(ephemeralSharedSecret.bytes)
|
let symmetricKey = try HMAC(key: salt, variant: .sha256).authenticate(ephemeralSharedSecret.bytes)
|
||||||
let closedGroupCiphertextMessageAsData = try AESGCM.decrypt(ivAndCiphertext, with: Data(symmetricKey))
|
let closedGroupCiphertextMessageAsData = try AESGCM.decrypt(ivAndCiphertext, with: Data(symmetricKey))
|
||||||
// 4. ) Parse the closed group ciphertext message
|
// 4. ) Parse the closed group ciphertext message
|
||||||
let closedGroupCiphertextMessage = ClosedGroupCiphertextMessage(_throws_with: closedGroupCiphertextMessageAsData)
|
let closedGroupCiphertextMessage = ClosedGroupCiphertextMessage(_throws_with: closedGroupCiphertextMessageAsData)
|
||||||
|
|
|
@ -7,11 +7,14 @@ public enum MessageReceiver {
|
||||||
case invalidMessage
|
case invalidMessage
|
||||||
case unknownMessage
|
case unknownMessage
|
||||||
case unknownEnvelopeType
|
case unknownEnvelopeType
|
||||||
case noUserPublicKey
|
case noUserX25519KeyPair
|
||||||
|
case noUserED25519KeyPair
|
||||||
|
case invalidSignature
|
||||||
case noData
|
case noData
|
||||||
case senderBlocked
|
case senderBlocked
|
||||||
case noThread
|
case noThread
|
||||||
case selfSend
|
case selfSend
|
||||||
|
case decryptionFailed
|
||||||
// Shared sender keys
|
// Shared sender keys
|
||||||
case invalidGroupPublicKey
|
case invalidGroupPublicKey
|
||||||
case noGroupPrivateKey
|
case noGroupPrivateKey
|
||||||
|
@ -19,7 +22,7 @@ public enum MessageReceiver {
|
||||||
|
|
||||||
public var isRetryable: Bool {
|
public var isRetryable: Bool {
|
||||||
switch self {
|
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
|
default: return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,15 +33,18 @@ public enum MessageReceiver {
|
||||||
case .invalidMessage: return "Invalid message."
|
case .invalidMessage: return "Invalid message."
|
||||||
case .unknownMessage: return "Unknown message type."
|
case .unknownMessage: return "Unknown message type."
|
||||||
case .unknownEnvelopeType: return "Unknown envelope 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 .noData: return "Received an empty envelope."
|
||||||
case .senderBlocked: return "Received a message from a blocked user."
|
case .senderBlocked: return "Received a message from a blocked user."
|
||||||
case .noThread: return "Couldn't find thread for message."
|
case .noThread: return "Couldn't find thread for message."
|
||||||
|
case .selfSend: return "Message addressed at self."
|
||||||
|
case .decryptionFailed: return "Decryption failed."
|
||||||
// Shared sender keys
|
// Shared sender keys
|
||||||
case .invalidGroupPublicKey: return "Invalid group public key."
|
case .invalidGroupPublicKey: return "Invalid group public key."
|
||||||
case .noGroupPrivateKey: return "Missing group private key."
|
case .noGroupPrivateKey: return "Missing group private key."
|
||||||
case .sharedSecretGenerationFailed: return "Couldn't generate a shared secret."
|
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!)
|
(plaintext, sender) = (envelope.content!, envelope.source!)
|
||||||
} else {
|
} else {
|
||||||
switch envelope.type {
|
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:
|
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
|
groupPublicKey = envelope.source
|
||||||
default: throw Error.unknownEnvelopeType
|
default: throw Error.unknownEnvelopeType
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import SessionProtocolKit
|
import SessionProtocolKit
|
||||||
import SessionUtilitiesKit
|
import SessionUtilitiesKit
|
||||||
|
import Sodium
|
||||||
|
|
||||||
internal extension MessageSender {
|
internal extension MessageSender {
|
||||||
|
|
||||||
|
@ -11,12 +12,25 @@ internal extension MessageSender {
|
||||||
return try cipher.throwswrapped_encryptMessage(recipientPublicKey: publicKey, deviceID: 1, paddedPlaintext: (plaintext as NSData).paddedMessageBody(),
|
return try cipher.throwswrapped_encryptMessage(recipientPublicKey: publicKey, deviceID: 1, paddedPlaintext: (plaintext as NSData).paddedMessageBody(),
|
||||||
senderCertificate: certificate, protocolContext: transaction, useFallbackSessionCipher: true)
|
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 verificationData = plaintext + Data(userED25519KeyPair.publicKey) + recipientX25519PublicKey
|
||||||
|
guard let signature = sodium.sign.signature(message: Bytes(verificationData), secretKey: userED25519KeyPair.secretKey) else { throw Error.signingFailed }
|
||||||
|
let plaintextWithMetadata = plaintext + Data(userED25519KeyPair.publicKey) + Data(signature)
|
||||||
|
guard let ciphertext = sodium.box.seal(message: Bytes(plaintextWithMetadata), recipientPublicKey: Bytes(recipientX25519PublicKey)) else { throw Error.encryptionFailed }
|
||||||
|
|
||||||
|
return Data(ciphertext)
|
||||||
|
}
|
||||||
|
|
||||||
static func encryptWithSharedSenderKeys(_ plaintext: Data, for groupPublicKey: String, using transaction: Any) throws -> Data {
|
static func encryptWithSharedSenderKeys(_ plaintext: Data, for groupPublicKey: String, using transaction: Any) throws -> Data {
|
||||||
// 1. ) Encrypt the data with the user's sender key
|
// 1. ) Encrypt the data with the user's sender key
|
||||||
guard let userPublicKey = SNMessagingKitConfiguration.shared.storage.getUserPublicKey() else {
|
guard let userPublicKey = SNMessagingKitConfiguration.shared.storage.getUserPublicKey() else {
|
||||||
SNLog("Couldn't find user key pair.")
|
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 (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))
|
let encryptedMessage = ClosedGroupCiphertextMessage(_throws_withIVAndCiphertext: ivAndCiphertext, senderPublicKey: Data(hex: userPublicKey), keyIndex: UInt32(keyIndex))
|
||||||
|
|
|
@ -10,7 +10,10 @@ public final class MessageSender : NSObject {
|
||||||
case invalidMessage
|
case invalidMessage
|
||||||
case protoConversionFailed
|
case protoConversionFailed
|
||||||
case proofOfWorkCalculationFailed
|
case proofOfWorkCalculationFailed
|
||||||
case noUserPublicKey
|
case noUserX25519KeyPair
|
||||||
|
case noUserED25519KeyPair
|
||||||
|
case signingFailed
|
||||||
|
case encryptionFailed
|
||||||
// Closed groups
|
// Closed groups
|
||||||
case noThread
|
case noThread
|
||||||
case noPrivateKey
|
case noPrivateKey
|
||||||
|
@ -18,7 +21,7 @@ public final class MessageSender : NSObject {
|
||||||
|
|
||||||
internal var isRetryable: Bool {
|
internal var isRetryable: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
case .invalidMessage, .protoConversionFailed, .proofOfWorkCalculationFailed, .invalidClosedGroupUpdate: return false
|
case .invalidMessage, .protoConversionFailed, .proofOfWorkCalculationFailed, .invalidClosedGroupUpdate, .signingFailed, .encryptionFailed: return false
|
||||||
default: return true
|
default: return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +31,10 @@ public final class MessageSender : NSObject {
|
||||||
case .invalidMessage: return "Invalid message."
|
case .invalidMessage: return "Invalid message."
|
||||||
case .protoConversionFailed: return "Couldn't convert message to proto."
|
case .protoConversionFailed: return "Couldn't convert message to proto."
|
||||||
case .proofOfWorkCalculationFailed: return "Proof of work calculation failed."
|
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
|
// Closed groups
|
||||||
case .noThread: return "Couldn't find a thread associated with the given group public key."
|
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."
|
case .noPrivateKey: return "Couldn't find a private key associated with the given group public key."
|
||||||
|
|
|
@ -19,7 +19,7 @@ public final class PushNotificationAPI : NSObject {
|
||||||
private override init() { }
|
private override init() { }
|
||||||
|
|
||||||
// MARK: Registration
|
// MARK: Registration
|
||||||
static func unregister(with token: Data, isForcedUpdate: Bool) -> Promise<Void> {
|
public static func unregister(_ token: Data) -> Promise<Void> {
|
||||||
let hexEncodedToken = token.toHexString()
|
let hexEncodedToken = token.toHexString()
|
||||||
let parameters = [ "token" : hexEncodedToken ]
|
let parameters = [ "token" : hexEncodedToken ]
|
||||||
let url = URL(string: "\(server)/unregister")!
|
let url = URL(string: "\(server)/unregister")!
|
||||||
|
@ -45,12 +45,12 @@ public final class PushNotificationAPI : NSObject {
|
||||||
return promise
|
return promise
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc(unregisterWithToken:isForcedUpdate:)
|
@objc(unregisterToken:)
|
||||||
public static func objc_unregister(with token: Data, isForcedUpdate: Bool) -> AnyPromise {
|
public static func objc_unregister(token: Data) -> AnyPromise {
|
||||||
return AnyPromise.from(unregister(with: token, isForcedUpdate: isForcedUpdate))
|
return AnyPromise.from(unregister(token))
|
||||||
}
|
}
|
||||||
|
|
||||||
static func register(with token: Data, publicKey: String, isForcedUpdate: Bool) -> Promise<Void> {
|
public static func register(with token: Data, publicKey: String, isForcedUpdate: Bool) -> Promise<Void> {
|
||||||
let hexEncodedToken = token.toHexString()
|
let hexEncodedToken = token.toHexString()
|
||||||
let userDefaults = UserDefaults.standard
|
let userDefaults = UserDefaults.standard
|
||||||
let oldToken = userDefaults[.deviceToken]
|
let oldToken = userDefaults[.deviceToken]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import SessionProtocolKit
|
import SessionProtocolKit
|
||||||
import PromiseKit
|
import PromiseKit
|
||||||
|
import Sodium
|
||||||
|
|
||||||
public protocol SessionMessagingKitStorageProtocol {
|
public protocol SessionMessagingKitStorageProtocol {
|
||||||
|
|
||||||
|
@ -15,6 +16,7 @@ public protocol SessionMessagingKitStorageProtocol {
|
||||||
|
|
||||||
func getUserPublicKey() -> String?
|
func getUserPublicKey() -> String?
|
||||||
func getUserKeyPair() -> ECKeyPair?
|
func getUserKeyPair() -> ECKeyPair?
|
||||||
|
func getUserED25519KeyPair() -> Box.KeyPair?
|
||||||
func getUserDisplayName() -> String?
|
func getUserDisplayName() -> String?
|
||||||
func getUserProfileKey() -> Data?
|
func getUserProfileKey() -> Data?
|
||||||
func getUserProfilePictureURL() -> String?
|
func getUserProfilePictureURL() -> String?
|
||||||
|
|
|
@ -277,7 +277,7 @@ NSString *const TSAccountManager_NeedsAccountAttributesUpdateKey = @"TSAccountMa
|
||||||
BOOL isUsingFullAPNs = [NSUserDefaults.standardUserDefaults boolForKey:@"isUsingFullAPNs"];
|
BOOL isUsingFullAPNs = [NSUserDefaults.standardUserDefaults boolForKey:@"isUsingFullAPNs"];
|
||||||
NSData *pushTokenAsData = [NSData dataFromHexString:pushToken];
|
NSData *pushTokenAsData = [NSData dataFromHexString:pushToken];
|
||||||
AnyPromise *promise = isUsingFullAPNs ? [LKPushNotificationAPI registerWithToken:pushTokenAsData hexEncodedPublicKey:self.localNumber isForcedUpdate:isForcedUpdate]
|
AnyPromise *promise = isUsingFullAPNs ? [LKPushNotificationAPI registerWithToken:pushTokenAsData hexEncodedPublicKey:self.localNumber isForcedUpdate:isForcedUpdate]
|
||||||
: [LKPushNotificationAPI unregisterWithToken:pushTokenAsData isForcedUpdate:isForcedUpdate];
|
: [LKPushNotificationAPI unregisterToken:pushTokenAsData];
|
||||||
promise
|
promise
|
||||||
.then(^() {
|
.then(^() {
|
||||||
successHandler();
|
successHandler();
|
||||||
|
|
|
@ -11,6 +11,7 @@ public enum LKUserDefaults {
|
||||||
|
|
||||||
public enum Date : Swift.String {
|
public enum Date : Swift.String {
|
||||||
case lastProfilePictureUpload
|
case lastProfilePictureUpload
|
||||||
|
case lastKeyPairMigrationNudge
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Double : Swift.String {
|
public enum Double : Swift.String {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public extension String {
|
public extension String {
|
||||||
|
|
||||||
var digitsOnly: String {
|
var digitsOnly: String {
|
||||||
return (self as NSString).digitsOnly()
|
return (self as NSString).digitsOnly()
|
||||||
}
|
}
|
||||||
|
|
|
@ -500,7 +500,6 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
|
||||||
|
|
||||||
- (UIView *)addBorderViewWithColor:(UIColor *)color strokeWidth:(CGFloat)strokeWidth cornerRadius:(CGFloat)cornerRadius
|
- (UIView *)addBorderViewWithColor:(UIColor *)color strokeWidth:(CGFloat)strokeWidth cornerRadius:(CGFloat)cornerRadius
|
||||||
{
|
{
|
||||||
|
|
||||||
UIView *borderView = [UIView new];
|
UIView *borderView = [UIView new];
|
||||||
borderView.userInteractionEnabled = NO;
|
borderView.userInteractionEnabled = NO;
|
||||||
borderView.backgroundColor = UIColor.clearColor;
|
borderView.backgroundColor = UIColor.clearColor;
|
||||||
|
|
|
@ -236,6 +236,7 @@
|
||||||
B82B408E239DC00D00A248E7 /* DisplayNameVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B408D239DC00D00A248E7 /* DisplayNameVC.swift */; };
|
B82B408E239DC00D00A248E7 /* DisplayNameVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B408D239DC00D00A248E7 /* DisplayNameVC.swift */; };
|
||||||
B82B4090239DD75000A248E7 /* RestoreVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B408F239DD75000A248E7 /* RestoreVC.swift */; };
|
B82B4090239DD75000A248E7 /* RestoreVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B408F239DD75000A248E7 /* RestoreVC.swift */; };
|
||||||
B82B4094239DF15900A248E7 /* ConversationTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B4093239DF15900A248E7 /* ConversationTitleView.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 */; };
|
B83F2B88240CB75A000A54AB /* UIImage+Scaling.swift in Sources */ = {isa = PBXBuildFile; fileRef = B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */; };
|
||||||
B84664F5235022F30083A1CD /* MentionUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84664F4235022F30083A1CD /* MentionUtilities.swift */; };
|
B84664F5235022F30083A1CD /* MentionUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84664F4235022F30083A1CD /* MentionUtilities.swift */; };
|
||||||
B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */; };
|
B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */; };
|
||||||
|
@ -244,6 +245,8 @@
|
||||||
B8566C63256F55930045A0B9 /* OWSLinkPreview+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8566C62256F55930045A0B9 /* OWSLinkPreview+Conversion.swift */; };
|
B8566C63256F55930045A0B9 /* OWSLinkPreview+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8566C62256F55930045A0B9 /* OWSLinkPreview+Conversion.swift */; };
|
||||||
B8566C6C256F60F50045A0B9 /* OWSUserProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2D1255B6DAF007E1867 /* OWSUserProfile.m */; };
|
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, ); }; };
|
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 */; };
|
B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; };
|
||||||
B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.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 */; };
|
B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */; };
|
||||||
|
@ -914,7 +917,6 @@
|
||||||
C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */; };
|
C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */; };
|
||||||
C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */; };
|
C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */; };
|
||||||
C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.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 */; };
|
C3ECBF7B257056B700EA7FCE /* Threading.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3ECBF7A257056B700EA7FCE /* Threading.swift */; };
|
||||||
C3F0A530255C80BC007BE2A3 /* NoopNotificationsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F0A52F255C80BC007BE2A3 /* NoopNotificationsManager.swift */; };
|
C3F0A530255C80BC007BE2A3 /* NoopNotificationsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F0A52F255C80BC007BE2A3 /* NoopNotificationsManager.swift */; };
|
||||||
D2179CFC16BB0B3A0006F3AB /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2179CFB16BB0B3A0006F3AB /* CoreTelephony.framework */; };
|
D2179CFC16BB0B3A0006F3AB /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2179CFB16BB0B3A0006F3AB /* CoreTelephony.framework */; };
|
||||||
|
@ -1362,6 +1364,7 @@
|
||||||
B82B408D239DC00D00A248E7 /* DisplayNameVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayNameVC.swift; sourceTree = "<group>"; };
|
B82B408D239DC00D00A248E7 /* DisplayNameVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayNameVC.swift; sourceTree = "<group>"; };
|
||||||
B82B408F239DD75000A248E7 /* RestoreVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestoreVC.swift; sourceTree = "<group>"; };
|
B82B408F239DD75000A248E7 /* RestoreVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestoreVC.swift; sourceTree = "<group>"; };
|
||||||
B82B4093239DF15900A248E7 /* ConversationTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationTitleView.swift; sourceTree = "<group>"; };
|
B82B4093239DF15900A248E7 /* ConversationTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationTitleView.swift; sourceTree = "<group>"; };
|
||||||
|
B837867F2586D296003CE78E /* KeyPairMigrationSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPairMigrationSheet.swift; sourceTree = "<group>"; };
|
||||||
B83F2B85240C7B8F000A54AB /* NewConversationButtonSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationButtonSet.swift; sourceTree = "<group>"; };
|
B83F2B85240C7B8F000A54AB /* NewConversationButtonSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationButtonSet.swift; sourceTree = "<group>"; };
|
||||||
B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Scaling.swift"; sourceTree = "<group>"; };
|
B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Scaling.swift"; sourceTree = "<group>"; };
|
||||||
B84072952565E9F50037CB17 /* TSOutgoingMessage+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSOutgoingMessage+Conversion.swift"; sourceTree = "<group>"; };
|
B84072952565E9F50037CB17 /* TSOutgoingMessage+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSOutgoingMessage+Conversion.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -1373,6 +1376,7 @@
|
||||||
B8544E3023D16CA500299F14 /* DeviceUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceUtilities.swift; sourceTree = "<group>"; };
|
B8544E3023D16CA500299F14 /* DeviceUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceUtilities.swift; sourceTree = "<group>"; };
|
||||||
B8544E3223D50E4900299F14 /* AppearanceUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceUtilities.swift; sourceTree = "<group>"; };
|
B8544E3223D50E4900299F14 /* AppearanceUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceUtilities.swift; sourceTree = "<group>"; };
|
||||||
B8566C62256F55930045A0B9 /* OWSLinkPreview+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OWSLinkPreview+Conversion.swift"; sourceTree = "<group>"; };
|
B8566C62256F55930045A0B9 /* OWSLinkPreview+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OWSLinkPreview+Conversion.swift"; sourceTree = "<group>"; };
|
||||||
|
B85A68B02587141A008CC492 /* Storage+Resetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Resetting.swift"; sourceTree = "<group>"; };
|
||||||
B86BD08323399ACF000F5AE3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = "<group>"; };
|
B86BD08323399ACF000F5AE3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = "<group>"; };
|
||||||
B86BD08523399CEF000F5AE3 /* SeedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedModal.swift; sourceTree = "<group>"; };
|
B86BD08523399CEF000F5AE3 /* SeedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedModal.swift; sourceTree = "<group>"; };
|
||||||
B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Interaction.swift"; sourceTree = "<group>"; };
|
B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Interaction.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -2603,7 +2607,7 @@
|
||||||
C31FFE56254A5FFE00F19441 /* KeyPairUtilities.swift */,
|
C31FFE56254A5FFE00F19441 /* KeyPairUtilities.swift */,
|
||||||
B84664F4235022F30083A1CD /* MentionUtilities.swift */,
|
B84664F4235022F30083A1CD /* MentionUtilities.swift */,
|
||||||
B886B4A82398BA1500211ABE /* QRCode.swift */,
|
B886B4A82398BA1500211ABE /* QRCode.swift */,
|
||||||
C3E7134E251C867C009649BB /* Sodium+Conversion.swift */,
|
B85A68B02587141A008CC492 /* Storage+Resetting.swift */,
|
||||||
B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */,
|
B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */,
|
||||||
B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */,
|
B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */,
|
||||||
C31A6C59247F214E001123EF /* UIView+Glow.swift */,
|
C31A6C59247F214E001123EF /* UIView+Glow.swift */,
|
||||||
|
@ -2621,6 +2625,7 @@
|
||||||
B88847BB23E10BC6009836D2 /* GroupMembersVC.swift */,
|
B88847BB23E10BC6009836D2 /* GroupMembersVC.swift */,
|
||||||
B8BB82A4238F627000BA5194 /* HomeVC.swift */,
|
B8BB82A4238F627000BA5194 /* HomeVC.swift */,
|
||||||
B8CCF63E23975CFB0091D419 /* JoinPublicChatVC.swift */,
|
B8CCF63E23975CFB0091D419 /* JoinPublicChatVC.swift */,
|
||||||
|
B837867F2586D296003CE78E /* KeyPairMigrationSheet.swift */,
|
||||||
B82B40872399EB0E00A248E7 /* LandingVC.swift */,
|
B82B40872399EB0E00A248E7 /* LandingVC.swift */,
|
||||||
C329FEEB24F7277900B1C64C /* LightModeSheet.swift */,
|
C329FEEB24F7277900B1C64C /* LightModeSheet.swift */,
|
||||||
B86BD08323399ACF000F5AE3 /* Modal.swift */,
|
B86BD08323399ACF000F5AE3 /* Modal.swift */,
|
||||||
|
@ -3372,6 +3377,7 @@
|
||||||
C33FDB91255A581200E217F9 /* ProtoUtils.h */,
|
C33FDB91255A581200E217F9 /* ProtoUtils.h */,
|
||||||
C33FDA6C255A57FA00E217F9 /* ProtoUtils.m */,
|
C33FDA6C255A57FA00E217F9 /* ProtoUtils.m */,
|
||||||
C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */,
|
C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */,
|
||||||
|
C3E7134E251C867C009649BB /* Sodium+Conversion.swift */,
|
||||||
C33FDB31255A580A00E217F9 /* SSKEnvironment.h */,
|
C33FDB31255A580A00E217F9 /* SSKEnvironment.h */,
|
||||||
C33FDAF4255A580600E217F9 /* SSKEnvironment.m */,
|
C33FDAF4255A580600E217F9 /* SSKEnvironment.m */,
|
||||||
C33FDB32255A580A00E217F9 /* SSKIncrementingIdFinder.swift */,
|
C33FDB32255A580A00E217F9 /* SSKIncrementingIdFinder.swift */,
|
||||||
|
@ -5253,6 +5259,7 @@
|
||||||
C3D9E3BE25676AD70040E4F3 /* TSAttachmentPointer.m in Sources */,
|
C3D9E3BE25676AD70040E4F3 /* TSAttachmentPointer.m in Sources */,
|
||||||
C3ECBF7B257056B700EA7FCE /* Threading.swift in Sources */,
|
C3ECBF7B257056B700EA7FCE /* Threading.swift in Sources */,
|
||||||
C32C5AAB256DBE8F003C73A2 /* TSIncomingMessage+Conversion.swift in Sources */,
|
C32C5AAB256DBE8F003C73A2 /* TSIncomingMessage+Conversion.swift in Sources */,
|
||||||
|
B866CE112581C1A900535CC4 /* Sodium+Conversion.swift in Sources */,
|
||||||
C32C5A88256DBCF9003C73A2 /* MessageReceiver+Handling.swift in Sources */,
|
C32C5A88256DBCF9003C73A2 /* MessageReceiver+Handling.swift in Sources */,
|
||||||
C32C5C1B256DC9E0003C73A2 /* General.swift in Sources */,
|
C32C5C1B256DC9E0003C73A2 /* General.swift in Sources */,
|
||||||
C32C5A02256DB658003C73A2 /* MessageSender+Convenience.swift in Sources */,
|
C32C5A02256DB658003C73A2 /* MessageSender+Convenience.swift in Sources */,
|
||||||
|
@ -5428,6 +5435,7 @@
|
||||||
C396DAF42518408B00FF6DC5 /* Parser.swift in Sources */,
|
C396DAF42518408B00FF6DC5 /* Parser.swift in Sources */,
|
||||||
4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */,
|
4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */,
|
||||||
4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */,
|
4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */,
|
||||||
|
B85A68B12587141A008CC492 /* Storage+Resetting.swift in Sources */,
|
||||||
3496955E219B605E00DCFE74 /* PhotoLibrary.swift in Sources */,
|
3496955E219B605E00DCFE74 /* PhotoLibrary.swift in Sources */,
|
||||||
45D231771DC7E8F10034FA89 /* SessionResetJob.swift in Sources */,
|
45D231771DC7E8F10034FA89 /* SessionResetJob.swift in Sources */,
|
||||||
340FC8A9204DAC8D007AEB0F /* NotificationSettingsOptionsViewController.m in Sources */,
|
340FC8A9204DAC8D007AEB0F /* NotificationSettingsOptionsViewController.m in Sources */,
|
||||||
|
@ -5522,7 +5530,6 @@
|
||||||
34D1F0831F8678AA0066283D /* ConversationInputTextView.m in Sources */,
|
34D1F0831F8678AA0066283D /* ConversationInputTextView.m in Sources */,
|
||||||
340FC8B6204DAC8D007AEB0F /* OWSQRCodeScanningViewController.m in Sources */,
|
340FC8B6204DAC8D007AEB0F /* OWSQRCodeScanningViewController.m in Sources */,
|
||||||
4CB5F26920F7D060004D1B42 /* MessageActions.swift in Sources */,
|
4CB5F26920F7D060004D1B42 /* MessageActions.swift in Sources */,
|
||||||
C3E7134F251C867C009649BB /* Sodium+Conversion.swift in Sources */,
|
|
||||||
340FC8B5204DAC8D007AEB0F /* AboutTableViewController.m in Sources */,
|
340FC8B5204DAC8D007AEB0F /* AboutTableViewController.m in Sources */,
|
||||||
C33100082558FF6D00070591 /* NewConversationButtonSet.swift in Sources */,
|
C33100082558FF6D00070591 /* NewConversationButtonSet.swift in Sources */,
|
||||||
B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */,
|
B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */,
|
||||||
|
@ -5532,6 +5539,7 @@
|
||||||
340FC8AC204DAC8D007AEB0F /* PrivacySettingsTableViewController.m in Sources */,
|
340FC8AC204DAC8D007AEB0F /* PrivacySettingsTableViewController.m in Sources */,
|
||||||
B88847BC23E10BC6009836D2 /* GroupMembersVC.swift in Sources */,
|
B88847BC23E10BC6009836D2 /* GroupMembersVC.swift in Sources */,
|
||||||
B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */,
|
B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */,
|
||||||
|
B83786802586D296003CE78E /* KeyPairMigrationSheet.swift in Sources */,
|
||||||
C35E8AAE2485E51D00ACB629 /* IP2Country.swift in Sources */,
|
C35E8AAE2485E51D00ACB629 /* IP2Country.swift in Sources */,
|
||||||
340FC8AE204DAC8D007AEB0F /* OWSSoundSettingsViewController.m in Sources */,
|
340FC8AE204DAC8D007AEB0F /* OWSSoundSettingsViewController.m in Sources */,
|
||||||
340FC8B0204DAC8D007AEB0F /* AddToBlockListViewController.m in Sources */,
|
340FC8B0204DAC8D007AEB0F /* AddToBlockListViewController.m in Sources */,
|
||||||
|
|
|
@ -3,6 +3,7 @@ import PromiseKit
|
||||||
|
|
||||||
extension MessageSender {
|
extension MessageSender {
|
||||||
|
|
||||||
|
// MARK: Durable
|
||||||
@objc(send:withAttachments:inThread:usingTransaction:)
|
@objc(send:withAttachments:inThread:usingTransaction:)
|
||||||
public static func send(_ message: VisibleMessage, with attachments: [SignalAttachment], in thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) {
|
public static func send(_ message: VisibleMessage, with attachments: [SignalAttachment], in thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) {
|
||||||
prep(attachments, for: message, using: transaction)
|
prep(attachments, for: message, using: transaction)
|
||||||
|
@ -15,8 +16,16 @@ extension MessageSender {
|
||||||
let destination = Message.Destination.from(thread)
|
let destination = Message.Destination.from(thread)
|
||||||
let job = MessageSendJob(message: message, destination: destination)
|
let job = MessageSendJob(message: message, destination: destination)
|
||||||
JobQueue.shared.add(job, using: transaction)
|
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:)
|
@objc(sendNonDurably:withAttachments:inThread:usingTransaction:)
|
||||||
public static func objc_sendNonDurably(_ message: VisibleMessage, with attachments: [SignalAttachment], in thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) -> AnyPromise {
|
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))
|
return AnyPromise.from(sendNonDurably(message, with: attachments, in: thread, using: transaction))
|
||||||
|
|
Loading…
Reference in New Issue