Use ServiceId in message sending code
Co-authored-by: Evan Hahn <69474926+EvanHahn-Signal@users.noreply.github.com>
This commit is contained in:
parent
5477d1706f
commit
ae2b290a07
@ -4235,6 +4235,13 @@ typedef OWSContact * (^OWSContactBlock)(SDSAnyWriteTransaction *transaction);
|
||||
}
|
||||
SignalServiceAddress *address = incomingSenderAddress;
|
||||
|
||||
ServiceIdObjC *_Nullable incomingSenderServiceId = incomingSenderAddress.serviceIdObjC;
|
||||
if (incomingSenderServiceId == nil) {
|
||||
OWSFailDebug(@"Missing incomingSenderServiceId.");
|
||||
return;
|
||||
}
|
||||
ServiceIdObjC *serviceId = incomingSenderServiceId;
|
||||
|
||||
DatabaseStorageWrite(SDSDatabaseStorage.shared, ^(SDSAnyWriteTransaction *transaction) {
|
||||
for (NSNumber *timestamp in timestamps) {
|
||||
NSString *randomText = [self randomText];
|
||||
@ -4256,7 +4263,7 @@ typedef OWSContact * (^OWSContactBlock)(SDSAnyWriteTransaction *transaction);
|
||||
TSOutgoingMessage *message = [messageBuilder buildWithTransaction:transaction];
|
||||
[message anyInsertWithTransaction:transaction];
|
||||
[message updateWithFakeMessageState:TSOutgoingMessageStateSent transaction:transaction];
|
||||
[message updateWithSentRecipient:address wasSentByUD:NO transaction:transaction];
|
||||
[message updateWithSentRecipient:serviceId wasSentByUD:NO transaction:transaction];
|
||||
[message updateWithDeliveredRecipient:address
|
||||
recipientDeviceId:0
|
||||
deliveryTimestamp:timestamp.unsignedLongLongValue
|
||||
|
||||
@ -39,12 +39,12 @@ protocol _ChangePhoneNumberPniManager_MessageSenderShim {
|
||||
func buildDeviceMessage(
|
||||
forMessagePlaintextContent messagePlaintextContent: Data?,
|
||||
messageEncryptionStyle: EncryptionStyle,
|
||||
recipientServiceId: ServiceId,
|
||||
recipientAccountId: String,
|
||||
recipientDeviceId: NSNumber,
|
||||
recipientId: String,
|
||||
serviceId: ServiceId,
|
||||
deviceId: NSNumber,
|
||||
isOnlineMessage: Bool,
|
||||
isTransientSenderKeyDistributionMessage: Bool,
|
||||
isStorySendMessage: Bool,
|
||||
isStoryMessage: Bool,
|
||||
isResendRequestMessage: Bool,
|
||||
udSendingParamsProvider: UDSendingParamsProvider?
|
||||
) throws -> DeviceMessage?
|
||||
@ -108,24 +108,24 @@ class _ChangePhoneNumberPniManager_MessageSenderWrapper: _ChangePhoneNumberPniMa
|
||||
func buildDeviceMessage(
|
||||
forMessagePlaintextContent messagePlaintextContent: Data?,
|
||||
messageEncryptionStyle: EncryptionStyle,
|
||||
recipientServiceId: ServiceId,
|
||||
recipientAccountId: String,
|
||||
recipientDeviceId: NSNumber,
|
||||
recipientId: String,
|
||||
serviceId: ServiceId,
|
||||
deviceId: NSNumber,
|
||||
isOnlineMessage: Bool,
|
||||
isTransientSenderKeyDistributionMessage: Bool,
|
||||
isStorySendMessage: Bool,
|
||||
isStoryMessage: Bool,
|
||||
isResendRequestMessage: Bool,
|
||||
udSendingParamsProvider: UDSendingParamsProvider?
|
||||
) throws -> DeviceMessage? {
|
||||
try messageSender.buildDeviceMessage(
|
||||
forMessagePlaintextContent: messagePlaintextContent,
|
||||
messageEncryptionStyle: messageEncryptionStyle,
|
||||
recipientAddress: SignalServiceAddress(recipientServiceId),
|
||||
recipientAccountId: recipientAccountId,
|
||||
recipientDeviceId: recipientDeviceId,
|
||||
recipientId: recipientId,
|
||||
serviceId: ServiceIdObjC(serviceId),
|
||||
deviceId: deviceId,
|
||||
isOnlineMessage: isOnlineMessage,
|
||||
isTransientSenderKeyDistributionMessage: isTransientSenderKeyDistributionMessage,
|
||||
isStorySendMessage: isStorySendMessage,
|
||||
isStoryMessage: isStoryMessage,
|
||||
isResendRequestMessage: isResendRequestMessage,
|
||||
udSendingParamsProvider: udSendingParamsProvider
|
||||
)
|
||||
|
||||
@ -297,8 +297,8 @@ class ChangePhoneNumberPniManagerImpl: ChangePhoneNumberPniManager {
|
||||
logger.info("Building device message for device with ID \(linkedDeviceId).")
|
||||
|
||||
return encryptPniChangeNumber(
|
||||
forRecipientAci: localAci,
|
||||
recipientAccountId: localAccountId,
|
||||
recipientId: localAccountId,
|
||||
recipientAci: localAci,
|
||||
recipientDeviceId: linkedDeviceId,
|
||||
identityKeyPair: pniIdentityKeyPair,
|
||||
signedPreKey: signedPreKey,
|
||||
@ -331,8 +331,8 @@ class ChangePhoneNumberPniManagerImpl: ChangePhoneNumberPniManager {
|
||||
/// The message for the linked device. If `nil`, indicates the device was
|
||||
/// invalid and should be skipped.
|
||||
private func encryptPniChangeNumber(
|
||||
forRecipientAci recipientAci: ServiceId,
|
||||
recipientAccountId: String,
|
||||
recipientId: String,
|
||||
recipientAci: ServiceId,
|
||||
recipientDeviceId: UInt32,
|
||||
identityKeyPair: ECKeyPair,
|
||||
signedPreKey: SignedPreKeyRecord,
|
||||
@ -357,12 +357,12 @@ class ChangePhoneNumberPniManagerImpl: ChangePhoneNumberPniManager {
|
||||
let deviceMessage: DeviceMessage? = try self.messageSender.buildDeviceMessage(
|
||||
forMessagePlaintextContent: plaintextContent,
|
||||
messageEncryptionStyle: .whisper,
|
||||
recipientServiceId: recipientAci,
|
||||
recipientAccountId: recipientAccountId,
|
||||
recipientDeviceId: NSNumber(value: recipientDeviceId),
|
||||
recipientId: recipientId,
|
||||
serviceId: recipientAci,
|
||||
deviceId: NSNumber(value: recipientDeviceId),
|
||||
isOnlineMessage: false,
|
||||
isTransientSenderKeyDistributionMessage: false,
|
||||
isStorySendMessage: false,
|
||||
isStoryMessage: false,
|
||||
isResendRequestMessage: false,
|
||||
udSendingParamsProvider: nil // Sync messages do not use UD
|
||||
)
|
||||
|
||||
@ -71,6 +71,7 @@ typedef NS_ENUM(NSInteger, EncryptionStyle) {
|
||||
@class SSKProtoAttachmentPointer;
|
||||
@class SSKProtoContentBuilder;
|
||||
@class SSKProtoDataMessageBuilder;
|
||||
@class ServiceIdObjC;
|
||||
@class SignalServiceAddress;
|
||||
@class TSOutgoingMessageBuilder;
|
||||
|
||||
@ -280,7 +281,7 @@ NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp
|
||||
#pragma mark - Update With... Methods
|
||||
|
||||
// This method is used to record a successful send to one recipient.
|
||||
- (void)updateWithSentRecipient:(SignalServiceAddress *)recipientAddress
|
||||
- (void)updateWithSentRecipient:(ServiceIdObjC *)serviceId
|
||||
wasSentByUD:(BOOL)wasSentByUD
|
||||
transaction:(SDSAnyWriteTransaction *)transaction;
|
||||
|
||||
|
||||
@ -667,13 +667,14 @@ NSUInteger const TSOutgoingMessageSchemaVersion = 1;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateWithSentRecipient:(SignalServiceAddress *)recipientAddress
|
||||
- (void)updateWithSentRecipient:(ServiceIdObjC *)serviceId
|
||||
wasSentByUD:(BOOL)wasSentByUD
|
||||
transaction:(SDSAnyWriteTransaction *)transaction
|
||||
{
|
||||
OWSAssertDebug(recipientAddress.isValid);
|
||||
OWSAssertDebug(serviceId);
|
||||
OWSAssertDebug(transaction);
|
||||
|
||||
SignalServiceAddress *recipientAddress = [[SignalServiceAddress alloc] initWithServiceIdObjC:serviceId];
|
||||
[self anyUpdateOutgoingMessageWithTransaction:transaction
|
||||
block:^(TSOutgoingMessage *message) {
|
||||
TSOutgoingMessageRecipientState *_Nullable recipientState
|
||||
|
||||
@ -335,6 +335,10 @@ public extension TSOutgoingMessage {
|
||||
throw OWSAssertionError("Missing local thread")
|
||||
}
|
||||
|
||||
guard let localUuid = Self.tsAccountManager.localUuid else {
|
||||
throw OWSAssertionError("Missing local uuid")
|
||||
}
|
||||
|
||||
guard let transcript = self.buildTranscriptSyncMessage(
|
||||
localThread: localThread,
|
||||
transaction: transaction
|
||||
@ -353,7 +357,7 @@ public extension TSOutgoingMessage {
|
||||
plaintextContent: plaintext,
|
||||
plaintextPayloadId: payloadId,
|
||||
thread: localThread,
|
||||
address: Self.tsAccountManager.localAddress!,
|
||||
serviceId: ServiceIdObjC(ServiceId(localUuid)),
|
||||
udSendingAccess: nil,
|
||||
localAddress: Self.tsAccountManager.localAddress!,
|
||||
sendErrorBlock: nil
|
||||
|
||||
@ -10,12 +10,12 @@ extension MessageSender {
|
||||
private static var maxSenderKeyEnvelopeSize: UInt64 { 256 * 1024 }
|
||||
|
||||
struct Recipient {
|
||||
let address: SignalServiceAddress
|
||||
let serviceId: ServiceId
|
||||
let devices: [UInt32]
|
||||
var protocolAddresses: [ProtocolAddress] {
|
||||
devices.compactMap {
|
||||
do {
|
||||
return try ProtocolAddress(from: address, deviceId: $0)
|
||||
return try ProtocolAddress(uuid: serviceId.uuidValue, deviceId: $0)
|
||||
} catch {
|
||||
owsFailDebug("\(error)")
|
||||
return nil
|
||||
@ -23,10 +23,13 @@ extension MessageSender {
|
||||
}
|
||||
}
|
||||
|
||||
init(address: SignalServiceAddress, transaction readTx: SDSAnyReadTransaction) {
|
||||
let recipient = SignalRecipient.get(address: address, mustHaveDevices: false, transaction: readTx)
|
||||
|
||||
self.address = address
|
||||
init(serviceId: ServiceId, transaction readTx: SDSAnyReadTransaction) {
|
||||
let recipient = SignalRecipient.get(
|
||||
address: SignalServiceAddress(serviceId),
|
||||
mustHaveDevices: false,
|
||||
transaction: readTx
|
||||
)
|
||||
self.serviceId = serviceId
|
||||
self.devices = recipient?.deviceIds ?? []
|
||||
}
|
||||
}
|
||||
@ -80,12 +83,12 @@ extension MessageSender {
|
||||
case NeedsSKDM
|
||||
case FanoutOnly
|
||||
}
|
||||
var participants: [SignalServiceAddress: ParticipantState]
|
||||
var participants: [ServiceIdObjC: ParticipantState]
|
||||
init(numberOfParticipants: Int) {
|
||||
self.participants = Dictionary(minimumCapacity: numberOfParticipants)
|
||||
}
|
||||
|
||||
init(fanoutOnlyParticipants: [SignalServiceAddress]) {
|
||||
init(fanoutOnlyParticipants: [ServiceIdObjC]) {
|
||||
self.participants = Dictionary(minimumCapacity: fanoutOnlyParticipants.count)
|
||||
super.init()
|
||||
|
||||
@ -93,20 +96,20 @@ extension MessageSender {
|
||||
}
|
||||
|
||||
@objc
|
||||
var fanoutParticipants: [SignalServiceAddress] {
|
||||
var fanoutParticipants: [ServiceIdObjC] {
|
||||
Array(participants.lazy.filter { $0.value == .FanoutOnly }.map { $0.key })
|
||||
}
|
||||
|
||||
@objc
|
||||
var allSenderKeyParticipants: [SignalServiceAddress] {
|
||||
var allSenderKeyParticipants: [ServiceIdObjC] {
|
||||
Array(participants.lazy.filter { $0.value != .FanoutOnly }.map { $0.key })
|
||||
}
|
||||
|
||||
var participantsNeedingSKDM: [SignalServiceAddress] {
|
||||
var participantsNeedingSKDM: [ServiceIdObjC] {
|
||||
Array(participants.lazy.filter { $0.value == .NeedsSKDM }.map { $0.key })
|
||||
}
|
||||
|
||||
var readyParticipants: [SignalServiceAddress] {
|
||||
var readyParticipants: [ServiceIdObjC] {
|
||||
Array(participants.lazy.filter { $0.value == .SenderKeyReady }.map { $0.key })
|
||||
}
|
||||
}
|
||||
@ -115,8 +118,8 @@ extension MessageSender {
|
||||
@objc
|
||||
func senderKeyStatus(
|
||||
for thread: TSThread,
|
||||
intendedRecipients: [SignalServiceAddress],
|
||||
udAccessMap: [SignalServiceAddress: OWSUDSendingAccess]
|
||||
intendedRecipients: [ServiceIdObjC],
|
||||
udAccessMap: [ServiceIdObjC: OWSUDSendingAccess]
|
||||
) -> SenderKeyStatus {
|
||||
guard !RemoteConfig.senderKeyKillSwitch else {
|
||||
Logger.info("Sender key kill switch activated. No recipients support sender key.")
|
||||
@ -131,18 +134,13 @@ extension MessageSender {
|
||||
let isCurrentKeyValid = senderKeyStore.isKeyValid(for: thread, readTx: readTx)
|
||||
let recipientsWithoutSenderKey = senderKeyStore.recipientsInNeedOfSenderKey(
|
||||
for: thread,
|
||||
addresses: intendedRecipients,
|
||||
readTx: readTx)
|
||||
serviceIds: intendedRecipients,
|
||||
readTx: readTx
|
||||
)
|
||||
|
||||
let senderKeyStatus = SenderKeyStatus(numberOfParticipants: intendedRecipients.count)
|
||||
let threadRecipients = thread.recipientAddresses(with: readTx)
|
||||
let threadRecipients = thread.recipientAddresses(with: readTx).compactMap { $0.serviceIdObjC }
|
||||
intendedRecipients.forEach { candidate in
|
||||
// Sender key is UUID-only
|
||||
guard candidate.isValid, candidate.uuid != nil, !candidate.isLocalAddress else {
|
||||
senderKeyStatus.participants[candidate] = .FanoutOnly
|
||||
return
|
||||
}
|
||||
|
||||
// Sender key requires that you're a full member of the group and you support UD
|
||||
guard
|
||||
threadRecipients.contains(candidate),
|
||||
@ -152,10 +150,16 @@ extension MessageSender {
|
||||
return
|
||||
}
|
||||
|
||||
guard !SignalServiceAddress(candidate.wrappedValue).isLocalAddress else {
|
||||
senderKeyStatus.participants[candidate] = .FanoutOnly
|
||||
owsFailBeta("Callers must not provide UD access for the local ACI.")
|
||||
return
|
||||
}
|
||||
|
||||
// If all registrationIds aren't valid, we should fallback to fanout
|
||||
// This should be removed once we've sorted out why there are invalid
|
||||
// registrationIds
|
||||
let registrationIdStatus = candidate.registrationIdStatus(transaction: readTx)
|
||||
let registrationIdStatus = Self.registrationIdStatus(for: candidate.wrappedValue, transaction: readTx)
|
||||
switch registrationIdStatus {
|
||||
case .valid:
|
||||
// All good, keep going.
|
||||
@ -190,9 +194,9 @@ extension MessageSender {
|
||||
payloadId: NSNumber?,
|
||||
thread: TSThread,
|
||||
status: SenderKeyStatus,
|
||||
udAccessMap: [SignalServiceAddress: OWSUDSendingAccess],
|
||||
udAccessMap: [ServiceIdObjC: OWSUDSendingAccess],
|
||||
senderCertificates: SenderCertificates,
|
||||
sendErrorBlock: @escaping (SignalServiceAddress, NSError) -> Void
|
||||
sendErrorBlock: @escaping (ServiceIdObjC, NSError) -> Void
|
||||
) -> AnyPromise {
|
||||
|
||||
AnyPromise(
|
||||
@ -214,23 +218,23 @@ extension MessageSender {
|
||||
payloadId: Int64?,
|
||||
thread: TSThread,
|
||||
status: SenderKeyStatus,
|
||||
udAccessMap: [SignalServiceAddress: OWSUDSendingAccess],
|
||||
udAccessMap: [ServiceIdObjC: OWSUDSendingAccess],
|
||||
senderCertificates: SenderCertificates,
|
||||
sendErrorBlock: @escaping (SignalServiceAddress, NSError) -> Void
|
||||
sendErrorBlock: @escaping (ServiceIdObjC, NSError) -> Void
|
||||
) -> Promise<Void> {
|
||||
|
||||
// Because of the way promises are combined further up the chain, we need to ensure that if
|
||||
// *any* send fails, the entire Promise rejcts. The error it rejects with doesn't really matter
|
||||
// and isn't consulted.
|
||||
let didHitAnyFailure = AtomicBool(false)
|
||||
let wrappedSendErrorBlock = { (address: SignalServiceAddress, error: Error) -> Void in
|
||||
Logger.info("Sender key send failed for \(address): \(error)")
|
||||
let wrappedSendErrorBlock = { (serviceId: ServiceIdObjC, error: Error) -> Void in
|
||||
Logger.info("Sender key send failed for \(serviceId): \(error)")
|
||||
_ = didHitAnyFailure.tryToSetFlag()
|
||||
|
||||
if let senderKeyError = error as? SenderKeyError {
|
||||
sendErrorBlock(address, senderKeyError.asSSKError)
|
||||
sendErrorBlock(serviceId, senderKeyError.asSSKError)
|
||||
} else {
|
||||
sendErrorBlock(address, (error as NSError))
|
||||
sendErrorBlock(serviceId, (error as NSError))
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,7 +244,7 @@ extension MessageSender {
|
||||
// pend indefinitely.
|
||||
let senderKeyGuarantee: Guarantee<Void>
|
||||
|
||||
senderKeyGuarantee = { () -> Guarantee<[SignalServiceAddress]> in
|
||||
senderKeyGuarantee = { () -> Guarantee<[ServiceIdObjC]> in
|
||||
// If none of our recipients need an SKDM let's just skip the database write.
|
||||
if status.participantsNeedingSKDM.count > 0 {
|
||||
return senderKeyDistributionPromise(
|
||||
@ -252,7 +256,7 @@ extension MessageSender {
|
||||
} else {
|
||||
return .value(status.readyParticipants)
|
||||
}
|
||||
}().then(on: senderKeyQueue) { (senderKeyRecipients: [SignalServiceAddress]) -> Guarantee<Void> in
|
||||
}().then(on: senderKeyQueue) { (senderKeyRecipients: [ServiceIdObjC]) -> Guarantee<Void> in
|
||||
guard senderKeyRecipients.count > 0 else {
|
||||
// Something went wrong with the SKDM promise. Exit early.
|
||||
owsAssertDebug(didHitAnyFailure.get())
|
||||
@ -262,43 +266,48 @@ extension MessageSender {
|
||||
return firstly { () -> Promise<SenderKeySendResult> in
|
||||
Logger.info("Sending sender key message with timestamp \(message.timestamp) to \(senderKeyRecipients)")
|
||||
|
||||
let senderCertificate = Self.senderCertificate(forSenderKeyRecipients: senderKeyRecipients,
|
||||
senderCertificates: senderCertificates)
|
||||
let senderCertificate = Self.senderCertificate(
|
||||
forSenderKeyRecipients: senderKeyRecipients,
|
||||
senderCertificates: senderCertificates
|
||||
)
|
||||
|
||||
return self.sendSenderKeyRequest(
|
||||
message: message,
|
||||
plaintext: plaintextContent,
|
||||
thread: thread,
|
||||
addresses: senderKeyRecipients,
|
||||
serviceIds: senderKeyRecipients,
|
||||
udAccessMap: udAccessMap,
|
||||
senderCertificate: senderCertificate)
|
||||
senderCertificate: senderCertificate
|
||||
)
|
||||
}.done(on: self.senderKeyQueue) { (sendResult: SenderKeySendResult) in
|
||||
Logger.info("Sender key message with timestamp \(message.timestamp) sent! Recipients: \(sendResult.successAddresses). Unregistered: \(sendResult.unregisteredAddresses)")
|
||||
Logger.info("Sender key message with timestamp \(message.timestamp) sent! Recipients: \(sendResult.successServiceIds). Unregistered: \(sendResult.unregisteredServiceIds)")
|
||||
|
||||
return try self.databaseStorage.write { writeTx in
|
||||
sendResult.unregisteredAddresses.forEach { address in
|
||||
self.markAddressAsUnregistered(address, message: message, thread: thread, transaction: writeTx)
|
||||
return self.databaseStorage.write { writeTx in
|
||||
sendResult.unregisteredServiceIds.forEach { serviceId in
|
||||
self.markAsUnregistered(serviceId: serviceId, message: message, thread: thread, transaction: writeTx)
|
||||
|
||||
let error = MessageSenderNoSuchSignalRecipientError()
|
||||
wrappedSendErrorBlock(address, error)
|
||||
wrappedSendErrorBlock(ServiceIdObjC(serviceId), error)
|
||||
}
|
||||
|
||||
try sendResult.success.forEach { recipient in
|
||||
guard let uuid = recipient.address.uuid else {
|
||||
throw OWSAssertionError("Invalid address")
|
||||
}
|
||||
sendResult.success.forEach { recipient in
|
||||
message.update(
|
||||
withSentRecipient: ServiceIdObjC(recipient.serviceId),
|
||||
wasSentByUD: true,
|
||||
transaction: writeTx
|
||||
)
|
||||
|
||||
message.update(withSentRecipient: recipient.address, wasSentByUD: true, transaction: writeTx)
|
||||
let recipientAddress = SignalServiceAddress(recipient.serviceId)
|
||||
|
||||
// If we're sending a story, we generally get a 200, even if the account
|
||||
// doesn't exist. Therefore, don't use this to mark accounts as registered.
|
||||
if !message.isStorySend {
|
||||
SignalRecipient.fetchOrCreate(for: recipient.address, trustLevel: .low, transaction: writeTx)
|
||||
SignalRecipient.fetchOrCreate(for: recipientAddress, trustLevel: .low, transaction: writeTx)
|
||||
.markAsRegistered(transaction: writeTx)
|
||||
}
|
||||
|
||||
self.profileManager.didSendOrReceiveMessage(
|
||||
from: recipient.address,
|
||||
from: recipientAddress,
|
||||
authedAccount: .implicit(),
|
||||
transaction: writeTx
|
||||
)
|
||||
@ -307,10 +316,11 @@ extension MessageSender {
|
||||
recipient.devices.forEach { deviceId in
|
||||
MessageSendLog.recordPendingDelivery(
|
||||
payloadId: payloadId,
|
||||
recipientUuid: uuid,
|
||||
recipientUuid: recipient.serviceId.uuidValue,
|
||||
recipientDeviceId: Int64(deviceId),
|
||||
message: message,
|
||||
transaction: writeTx)
|
||||
transaction: writeTx
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -332,15 +342,18 @@ extension MessageSender {
|
||||
}
|
||||
}
|
||||
|
||||
private static func senderCertificate(forSenderKeyRecipients senderKeyRecipients: [SignalServiceAddress],
|
||||
senderCertificates: SenderCertificates) -> SenderCertificate {
|
||||
private static func senderCertificate(
|
||||
forSenderKeyRecipients senderKeyRecipients: [ServiceIdObjC],
|
||||
senderCertificates: SenderCertificates
|
||||
) -> SenderCertificate {
|
||||
let phoneNumberSharingMode = udManager.phoneNumberSharingMode
|
||||
switch phoneNumberSharingMode {
|
||||
case .everybody:
|
||||
return senderCertificates.defaultCert
|
||||
case .contactsOnly:
|
||||
let areAllSystemContacts: Bool = databaseStorage.read { transaction in
|
||||
for address in senderKeyRecipients {
|
||||
for serviceId in senderKeyRecipients {
|
||||
let address = SignalServiceAddress(serviceId.wrappedValue)
|
||||
if !Self.contactsManager.isSystemContact(address: address, transaction: transaction) {
|
||||
return false
|
||||
}
|
||||
@ -363,14 +376,14 @@ extension MessageSender {
|
||||
//
|
||||
// Returns the list of all recipients ready for the SenderKeyMessage.
|
||||
private func senderKeyDistributionPromise(
|
||||
recipients: [SignalServiceAddress],
|
||||
recipients: [ServiceIdObjC],
|
||||
thread: TSThread,
|
||||
originalMessage: TSOutgoingMessage,
|
||||
udAccessMap: [SignalServiceAddress: OWSUDSendingAccess],
|
||||
sendErrorBlock: @escaping (SignalServiceAddress, Error) -> Void
|
||||
) -> Guarantee<[SignalServiceAddress]> {
|
||||
udAccessMap: [ServiceIdObjC: OWSUDSendingAccess],
|
||||
sendErrorBlock: @escaping (ServiceIdObjC, Error) -> Void
|
||||
) -> Guarantee<[ServiceIdObjC]> {
|
||||
|
||||
var recipientsNotNeedingSKDM: Set<SignalServiceAddress> = Set()
|
||||
var recipientsNotNeedingSKDM: Set<ServiceIdObjC> = Set()
|
||||
return databaseStorage.write(.promise) { writeTx -> [OWSMessageSend] in
|
||||
// Here we fetch all of the recipients that need an SKDM
|
||||
// We then construct an OWSMessageSend for each recipient that needs an SKDM.
|
||||
@ -386,8 +399,9 @@ extension MessageSender {
|
||||
|
||||
let recipientsNeedingSKDM = self.senderKeyStore.recipientsInNeedOfSenderKey(
|
||||
for: thread,
|
||||
addresses: recipients,
|
||||
readTx: writeTx)
|
||||
serviceIds: recipients,
|
||||
readTx: writeTx
|
||||
)
|
||||
recipientsNotNeedingSKDM = Set(recipients).subtracting(recipientsNeedingSKDM)
|
||||
|
||||
guard !recipientsNeedingSKDM.isEmpty else { return [] }
|
||||
@ -395,17 +409,22 @@ extension MessageSender {
|
||||
throw OWSAssertionError("Couldn't build SKDM")
|
||||
}
|
||||
|
||||
return recipientsNeedingSKDM.map { address in
|
||||
return recipientsNeedingSKDM.map { serviceId in
|
||||
if let groupThread = thread as? TSGroupThread {
|
||||
Logger.info("Sending SKDM to \(address) for group thread \(groupThread.groupId)")
|
||||
Logger.info("Sending SKDM to \(serviceId) for group thread \(groupThread.groupId)")
|
||||
} else {
|
||||
Logger.info("Sending SKDM to \(address) for thread \(thread.uniqueId)")
|
||||
Logger.info("Sending SKDM to \(serviceId) for thread \(thread.uniqueId)")
|
||||
}
|
||||
|
||||
let contactThread = TSContactThread.getOrCreateThread(withContactAddress: address, transaction: writeTx)
|
||||
let contactThread = TSContactThread.getOrCreateThread(
|
||||
withContactAddress: SignalServiceAddress(serviceId.wrappedValue),
|
||||
transaction: writeTx
|
||||
)
|
||||
let skdmMessage = OWSOutgoingSenderKeyDistributionMessage(
|
||||
thread: contactThread,
|
||||
senderKeyDistributionMessageBytes: skdmBytes, transaction: writeTx)
|
||||
senderKeyDistributionMessageBytes: skdmBytes,
|
||||
transaction: writeTx
|
||||
)
|
||||
skdmMessage.configureAsSentOnBehalfOf(originalMessage, in: thread)
|
||||
|
||||
let plaintext = skdmMessage.buildPlainTextData(contactThread, transaction: writeTx)
|
||||
@ -423,8 +442,8 @@ extension MessageSender {
|
||||
plaintextContent: plaintext,
|
||||
plaintextPayloadId: payloadId,
|
||||
thread: contactThread,
|
||||
address: address,
|
||||
udSendingAccess: udAccessMap[address],
|
||||
serviceId: serviceId,
|
||||
udSendingAccess: udAccessMap[serviceId],
|
||||
localAddress: localAddress,
|
||||
sendErrorBlock: nil)
|
||||
}
|
||||
@ -449,28 +468,33 @@ extension MessageSender {
|
||||
}.recover(on: self.senderKeyQueue) { error -> Promise<OWSMessageSend> in
|
||||
if error is MessageSenderNoSuchSignalRecipientError {
|
||||
self.databaseStorage.write { transaction in
|
||||
self.markAddressAsUnregistered(messageSend.address, message: originalMessage, thread: thread, transaction: transaction)
|
||||
self.markAsUnregistered(
|
||||
serviceId: messageSend.serviceId.wrappedValue,
|
||||
message: originalMessage,
|
||||
thread: thread,
|
||||
transaction: transaction
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Note that we still rethrow. It's just easier to access the address
|
||||
// while we still have the messageSend in scope.
|
||||
let wrappedError = SenderKeyError.recipientSKDMFailed(error)
|
||||
sendErrorBlock(messageSend.address, wrappedError)
|
||||
sendErrorBlock(messageSend.serviceId, wrappedError)
|
||||
throw wrappedError
|
||||
}
|
||||
})
|
||||
}
|
||||
}.map(on: self.senderKeyQueue) { resultArray -> [SignalServiceAddress] in
|
||||
}.map(on: self.senderKeyQueue) { resultArray -> [ServiceIdObjC] in
|
||||
// This is a hot path, so we do a bit of a dance here to prepare all of the successful send
|
||||
// info before opening the write transaction. We need the recipient address and the SKDM
|
||||
// timestamp.
|
||||
let successfulSendInfo: [(recipient: SignalServiceAddress, timestamp: UInt64)]
|
||||
let successfulSendInfo: [(recipient: ServiceIdObjC, timestamp: UInt64)]
|
||||
|
||||
successfulSendInfo = resultArray.compactMap { result in
|
||||
switch result {
|
||||
case let .success(messageSend):
|
||||
return (recipient: messageSend.address, timestamp: messageSend.message.timestamp)
|
||||
return (recipient: messageSend.serviceId, timestamp: messageSend.message.timestamp)
|
||||
case .failure:
|
||||
return nil
|
||||
}
|
||||
@ -479,13 +503,16 @@ extension MessageSender {
|
||||
if successfulSendInfo.count > 0 {
|
||||
try self.databaseStorage.write { writeTx in
|
||||
try successfulSendInfo.forEach {
|
||||
try self.senderKeyStore.recordSenderKeySent(for: thread,
|
||||
to: $0.recipient,
|
||||
timestamp: $0.timestamp,
|
||||
writeTx: writeTx)
|
||||
try self.senderKeyStore.recordSenderKeySent(
|
||||
for: thread,
|
||||
to: $0.recipient,
|
||||
timestamp: $0.timestamp,
|
||||
writeTx: writeTx
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We want to return all recipients that are now ready for sender key
|
||||
return Array(recipientsNotNeedingSKDM) + successfulSendInfo.map { $0.recipient }
|
||||
|
||||
@ -502,8 +529,8 @@ extension MessageSender {
|
||||
let success: [Recipient]
|
||||
let unregistered: [Recipient]
|
||||
|
||||
var successAddresses: [SignalServiceAddress] { success.map { $0.address } }
|
||||
var unregisteredAddresses: [SignalServiceAddress] { unregistered.map { $0.address } }
|
||||
var successServiceIds: [ServiceId] { success.map { $0.serviceId } }
|
||||
var unregisteredServiceIds: [ServiceId] { unregistered.map { $0.serviceId } }
|
||||
}
|
||||
|
||||
// Encrypts and sends the message using SenderKey
|
||||
@ -513,8 +540,8 @@ extension MessageSender {
|
||||
message: TSOutgoingMessage,
|
||||
plaintext: Data?,
|
||||
thread: TSThread,
|
||||
addresses: [SignalServiceAddress],
|
||||
udAccessMap: [SignalServiceAddress: OWSUDSendingAccess],
|
||||
serviceIds: [ServiceIdObjC],
|
||||
udAccessMap: [ServiceIdObjC: OWSUDSendingAccess],
|
||||
senderCertificate: SenderCertificate
|
||||
) -> Promise<SenderKeySendResult> {
|
||||
guard let plaintext = plaintext else {
|
||||
@ -522,14 +549,15 @@ extension MessageSender {
|
||||
}
|
||||
|
||||
return self.databaseStorage.write(.promise) { writeTx -> ([Recipient], Data) in
|
||||
let recipients = addresses.map { Recipient(address: $0, transaction: writeTx) }
|
||||
let recipients = serviceIds.map { Recipient(serviceId: $0.wrappedValue, transaction: writeTx) }
|
||||
let ciphertext = try self.senderKeyMessageBody(
|
||||
plaintext: plaintext,
|
||||
message: message,
|
||||
thread: thread,
|
||||
recipients: recipients,
|
||||
senderCertificate: senderCertificate,
|
||||
transaction: writeTx)
|
||||
transaction: writeTx
|
||||
)
|
||||
return (recipients, ciphertext)
|
||||
|
||||
}.then(on: senderKeyQueue) { (recipients: [Recipient], ciphertext: Data) -> Promise<SenderKeySendResult> in
|
||||
@ -557,7 +585,7 @@ extension MessageSender {
|
||||
isStory: Bool,
|
||||
thread: TSThread,
|
||||
recipients: [Recipient],
|
||||
udAccessMap: [SignalServiceAddress: OWSUDSendingAccess],
|
||||
udAccessMap: [ServiceIdObjC: OWSUDSendingAccess],
|
||||
senderCertificate: SenderCertificate,
|
||||
remainingAttempts: UInt
|
||||
) -> Promise<SenderKeySendResult> {
|
||||
@ -578,15 +606,8 @@ extension MessageSender {
|
||||
}
|
||||
let response = try Self.decodeSuccessResponse(data: response.responseBodyData)
|
||||
let uuids404 = Set(response.uuids404)
|
||||
|
||||
let successful = try recipients.filter {
|
||||
guard let uuid = $0.address.uuid else { throw OWSAssertionError("Invalid address") }
|
||||
return !uuids404.contains(uuid)
|
||||
}
|
||||
let unregistered = try recipients.filter {
|
||||
guard let uuid = $0.address.uuid else { throw OWSAssertionError("Invalid address") }
|
||||
return uuids404.contains(uuid)
|
||||
}
|
||||
let successful = recipients.filter { !uuids404.contains($0.serviceId.uuidValue) }
|
||||
let unregistered = recipients.filter { uuids404.contains($0.serviceId.uuidValue) }
|
||||
return SenderKeySendResult(success: successful, unregistered: unregistered)
|
||||
}.recover(on: senderKeyQueue) { error -> Promise<SenderKeySendResult> in
|
||||
let retryIfPossible = { () throws -> Promise<SenderKeySendResult> in
|
||||
@ -729,11 +750,13 @@ extension MessageSender {
|
||||
isStory: Bool,
|
||||
thread: TSThread,
|
||||
recipients: [Recipient],
|
||||
udAccessMap: [SignalServiceAddress: OWSUDSendingAccess]
|
||||
udAccessMap: [ServiceIdObjC: OWSUDSendingAccess]
|
||||
) throws -> Promise<HTTPResponse> {
|
||||
|
||||
// Sender key messages use an access key composed of every recipient's individual access key.
|
||||
let allAccessKeys = recipients.compactMap { udAccessMap[$0.address]?.udAccess.senderKeyUDAccessKey }
|
||||
let allAccessKeys = recipients.compactMap {
|
||||
udAccessMap[ServiceIdObjC($0.serviceId)]?.udAccess.senderKeyUDAccessKey
|
||||
}
|
||||
guard recipients.count == allAccessKeys.count else {
|
||||
throw OWSAssertionError("Incomplete access key set")
|
||||
}
|
||||
@ -803,7 +826,7 @@ extension MessageSender {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension SignalServiceAddress {
|
||||
fileprivate extension MessageSender {
|
||||
|
||||
enum RegistrationIdStatus {
|
||||
/// The address has a session with a valid registration id
|
||||
@ -826,16 +849,16 @@ fileprivate extension SignalServiceAddress {
|
||||
/// Also check for missing sessions (shouldn't happen if we've gotten this far, since
|
||||
/// SenderKeyStore already said this address has previous Sender Key sends). We should
|
||||
/// investigate how this ever happened, but for now fall back to sending another SKDM.
|
||||
func registrationIdStatus(transaction readTx: SDSAnyReadTransaction) -> RegistrationIdStatus {
|
||||
let candidateDevices = MessageSender.Recipient(address: self, transaction: readTx).devices
|
||||
static func registrationIdStatus(for serviceId: ServiceId, transaction: SDSAnyReadTransaction) -> RegistrationIdStatus {
|
||||
let candidateDevices = MessageSender.Recipient(serviceId: serviceId, transaction: transaction).devices
|
||||
let sessionStore = signalProtocolStore(for: .aci).sessionStore
|
||||
for deviceId in candidateDevices {
|
||||
do {
|
||||
guard
|
||||
let sessionRecord = try sessionStore.loadSession(
|
||||
for: self,
|
||||
for: SignalServiceAddress(serviceId),
|
||||
deviceId: Int32(deviceId),
|
||||
transaction: readTx
|
||||
transaction: transaction
|
||||
),
|
||||
sessionRecord.hasCurrentState
|
||||
else { return .noSession }
|
||||
@ -848,7 +871,7 @@ fileprivate extension SignalServiceAddress {
|
||||
} catch {
|
||||
// An error is never thrown on nil result; only if there's something
|
||||
// on disk but parsing fails.
|
||||
owsFailDebug("Failed to fetch registrationId for \(self): \(error)")
|
||||
owsFailDebug("Failed to fetch registrationId for \(serviceId): \(error)")
|
||||
return .invalid
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,17 +111,17 @@ NS_SWIFT_NAME(OutgoingAttachmentInfo)
|
||||
/// could not be built due to an invalid device ID.
|
||||
- (nullable DeviceMessage *)buildDeviceMessageForMessagePlaintextContent:(nullable NSData *)messagePlaintextContent
|
||||
messageEncryptionStyle:(EncryptionStyle)messageEncryptionStyle
|
||||
recipientAddress:(SignalServiceAddress *)recipientAddress
|
||||
recipientAccountId:(NSString *)recipientAccountId
|
||||
recipientDeviceId:(NSNumber *)recipientDeviceId
|
||||
recipientId:(NSString *)recipientId
|
||||
serviceId:(ServiceIdObjC *)serviceId
|
||||
deviceId:(NSNumber *)deviceId
|
||||
isOnlineMessage:(BOOL)isOnlineMessage
|
||||
isTransientSenderKeyDistributionMessage:(BOOL)isTransientSenderKeyDistributionMessage
|
||||
isStorySendMessage:(BOOL)isStorySendMessage
|
||||
isStoryMessage:(BOOL)isStoryMessage
|
||||
isResendRequestMessage:(BOOL)isResendRequestMessage
|
||||
udSendingParamsProvider:
|
||||
(nullable id<UDSendingParamsProvider>)udSendingParamsProvider
|
||||
error:(NSError **)errorHandle;
|
||||
__attribute__((swift_error(nonnull_error)));
|
||||
error:(NSError **)errorHandle
|
||||
__attribute__((swift_error(nonnull_error)));
|
||||
|
||||
+ (NSOperationQueuePriority)queuePriorityForMessage:(TSOutgoingMessage *)message;
|
||||
|
||||
|
||||
@ -570,14 +570,14 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
});
|
||||
}
|
||||
|
||||
- (AnyPromise *)sendPromiseForAddresses:(NSArray<SignalServiceAddress *> *)addresses
|
||||
message:(TSOutgoingMessage *)message
|
||||
thread:(TSThread *)thread
|
||||
senderCertificates:(nullable SenderCertificates *)senderCertificates
|
||||
sendErrorBlock:(void (^_Nonnull)(SignalServiceAddress *address, NSError *))sendErrorBlock
|
||||
- (AnyPromise *)sendPromiseForServiceIds:(NSArray<ServiceIdObjC *> *)serviceIds
|
||||
message:(TSOutgoingMessage *)message
|
||||
thread:(TSThread *)thread
|
||||
senderCertificates:(nullable SenderCertificates *)senderCertificates
|
||||
sendErrorBlock:(void (^_Nonnull)(ServiceIdObjC *address, NSError *))sendErrorBlock
|
||||
{
|
||||
OWSAssertDebug(!NSThread.isMainThread);
|
||||
OWSAssertDebug(addresses.count > 0);
|
||||
OWSAssertDebug(serviceIds.count > 0);
|
||||
OWSAssertDebug(message);
|
||||
OWSAssertDebug(thread);
|
||||
|
||||
@ -592,31 +592,32 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
OWSLogDebug(@"built message: %@ plainTextData.length: %lu", [message class], (unsigned long)plaintext.length);
|
||||
|
||||
// 2. Gather "ud sending access".
|
||||
NSMutableDictionary<SignalServiceAddress *, OWSUDSendingAccess *> *sendingAccessMap = [NSMutableDictionary new];
|
||||
NSMutableDictionary<ServiceIdObjC *, OWSUDSendingAccess *> *sendingAccessMap = [NSMutableDictionary new];
|
||||
if (senderCertificates != nil) {
|
||||
for (SignalServiceAddress *address in addresses) {
|
||||
if (!address.isLocalAddress) {
|
||||
if (message.isStorySend) {
|
||||
sendingAccessMap[address] = [self.udManager storySendingAccessForAddress:address
|
||||
senderCertificates:senderCertificates];
|
||||
} else {
|
||||
sendingAccessMap[address] = [self.udManager udSendingAccessForAddress:address
|
||||
requireSyncAccess:YES
|
||||
senderCertificates:senderCertificates];
|
||||
}
|
||||
for (ServiceIdObjC *serviceId in serviceIds) {
|
||||
if ([self.tsAccountManager.localUuid isEqual:serviceId.uuidValue]) {
|
||||
continue;
|
||||
}
|
||||
if (message.isStorySend) {
|
||||
sendingAccessMap[serviceId] = [self.udManager storySendingAccessFor:serviceId
|
||||
senderCertificates:senderCertificates];
|
||||
} else {
|
||||
sendingAccessMap[serviceId] = [self.udManager udSendingAccessFor:serviceId
|
||||
requireSyncAccess:YES
|
||||
senderCertificates:senderCertificates];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. If we have any participants that support sender key, build a promise for their send.
|
||||
SenderKeyStatus *senderKeyStatus = [self senderKeyStatusFor:thread
|
||||
intendedRecipients:addresses
|
||||
intendedRecipients:serviceIds
|
||||
udAccessMap:sendingAccessMap];
|
||||
|
||||
AnyPromise *_Nullable senderKeyMessagePromise = nil;
|
||||
NSArray<SignalServiceAddress *> *senderKeyAddresses = senderKeyStatus.allSenderKeyParticipants;
|
||||
NSArray<SignalServiceAddress *> *fanoutSendAddresses = senderKeyStatus.fanoutParticipants;
|
||||
if (thread.usesSenderKey && senderKeyAddresses.count >= 2 && message.canSendWithSenderKey) {
|
||||
NSArray<ServiceIdObjC *> *senderKeyServiceIds = senderKeyStatus.allSenderKeyParticipants;
|
||||
NSArray<ServiceIdObjC *> *fanoutServiceIds = senderKeyStatus.fanoutParticipants;
|
||||
if (thread.usesSenderKey && senderKeyServiceIds.count >= 2 && message.canSendWithSenderKey) {
|
||||
senderKeyMessagePromise = [self senderKeyMessageSendPromiseWithMessage:message
|
||||
plaintextContent:plaintext
|
||||
payloadId:plaintextPayloadId
|
||||
@ -627,12 +628,12 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
sendErrorBlock:sendErrorBlock];
|
||||
|
||||
OWSLogDebug(@"%lu / %lu recipients for message: %llu support sender key.",
|
||||
senderKeyAddresses.count,
|
||||
addresses.count,
|
||||
senderKeyServiceIds.count,
|
||||
serviceIds.count,
|
||||
message.timestamp);
|
||||
} else {
|
||||
senderKeyAddresses = @[];
|
||||
fanoutSendAddresses = addresses;
|
||||
senderKeyServiceIds = @[];
|
||||
fanoutServiceIds = serviceIds;
|
||||
if (!message.canSendWithSenderKey) {
|
||||
OWSLogInfo(
|
||||
@"Last sender key send attempt failed for message %llu. Falling back to fanout.", message.timestamp);
|
||||
@ -640,21 +641,21 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
OWSLogDebug(@"Sender key not supported for message %llu", message.timestamp);
|
||||
}
|
||||
}
|
||||
OWSAssertDebug((fanoutSendAddresses.count + senderKeyAddresses.count) == addresses.count);
|
||||
OWSAssertDebug((fanoutServiceIds.count + senderKeyServiceIds.count) == serviceIds.count);
|
||||
|
||||
// 4. Build a "OWSMessageSend" for each non-senderKey recipient.
|
||||
NSMutableArray<OWSMessageSend *> *messageSends = [NSMutableArray new];
|
||||
for (SignalServiceAddress *address in fanoutSendAddresses) {
|
||||
OWSUDSendingAccess *_Nullable udSendingAccess = sendingAccessMap[address];
|
||||
for (ServiceIdObjC *serviceId in fanoutServiceIds) {
|
||||
OWSUDSendingAccess *_Nullable udSendingAccess = sendingAccessMap[serviceId];
|
||||
OWSMessageSend *messageSend =
|
||||
[[OWSMessageSend alloc] initWithMessage:message
|
||||
plaintextContent:plaintext
|
||||
plaintextPayloadId:plaintextPayloadId
|
||||
thread:thread
|
||||
address:address
|
||||
serviceId:serviceId
|
||||
udSendingAccess:udSendingAccess
|
||||
localAddress:self.tsAccountManager.localAddress
|
||||
sendErrorBlock:^(NSError *error) { sendErrorBlock(address, error); }];
|
||||
sendErrorBlock:^(NSError *error) { sendErrorBlock(serviceId, error); }];
|
||||
[messageSends addObject:messageSend];
|
||||
}
|
||||
|
||||
@ -718,7 +719,7 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
};
|
||||
|
||||
TSThread *thread = sendInfo.thread;
|
||||
NSArray<SignalServiceAddress *> *recipientAddresses = sendInfo.recipients;
|
||||
NSArray<ServiceIdObjC *> *recipientServiceIds = sendInfo.serviceIds;
|
||||
SenderCertificates *senderCertificates = sendInfo.senderCertificates;
|
||||
|
||||
BOOL canSendToThread = NO;
|
||||
@ -748,7 +749,7 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
// (In particular, regular data messages are sent via their implicit sync message only.)
|
||||
if (contactThread.contactAddress.isLocalAddress && !message.canSendToLocalAddress) {
|
||||
// Send to self.
|
||||
OWSAssertDebug(sendInfo.recipients.count == 1);
|
||||
OWSAssertDebug(recipientServiceIds.count == 1);
|
||||
OWSLogInfo(@"dropping %@ sent to local address (expected to be sent by sync message)", [message class]);
|
||||
// Don't mark self-sent messages as read (or sent) until the sync transcript is sent.
|
||||
successHandler();
|
||||
@ -756,7 +757,7 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
}
|
||||
}
|
||||
|
||||
if (recipientAddresses.count < 1) {
|
||||
if (recipientServiceIds.count < 1) {
|
||||
// All recipients are already sent or can be skipped.
|
||||
// NOTE: We might still need to send a sync transcript.
|
||||
successHandler();
|
||||
@ -765,20 +766,20 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
|
||||
BOOL isNonContactThread = thread.isNonContactThread;
|
||||
NSMutableArray<NSError *> *sendErrors = [NSMutableArray array];
|
||||
NSMutableDictionary<SignalServiceAddress *, NSError *> *sendErrorPerRecipient = [NSMutableDictionary dictionary];
|
||||
NSMutableDictionary<ServiceIdObjC *, NSError *> *sendErrorPerRecipient = [NSMutableDictionary dictionary];
|
||||
|
||||
[self unlockPreKeyUpdateFailuresPromise]
|
||||
.thenInBackground(^(id value) {
|
||||
return [self sendPromiseForAddresses:recipientAddresses
|
||||
message:message
|
||||
thread:thread
|
||||
senderCertificates:senderCertificates
|
||||
sendErrorBlock:^(SignalServiceAddress *address, NSError *error) {
|
||||
@synchronized(sendErrors) {
|
||||
[sendErrors addObject:error];
|
||||
sendErrorPerRecipient[address] = error;
|
||||
}
|
||||
}];
|
||||
return [self sendPromiseForServiceIds:recipientServiceIds
|
||||
message:message
|
||||
thread:thread
|
||||
senderCertificates:senderCertificates
|
||||
sendErrorBlock:^(ServiceIdObjC *serviceId, NSError *error) {
|
||||
@synchronized(sendErrors) {
|
||||
[sendErrors addObject:error];
|
||||
sendErrorPerRecipient[serviceId] = error;
|
||||
}
|
||||
}];
|
||||
})
|
||||
.doneInBackground(^(id value) { successHandler(); })
|
||||
.catchInBackground(^(id failure) {
|
||||
@ -787,7 +788,7 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
NSError *firstNonRetryableError = nil;
|
||||
|
||||
NSArray<NSError *> *sendErrorsCopy;
|
||||
NSDictionary<SignalServiceAddress *, NSError *> *sendErrorPerRecipientCopy;
|
||||
NSDictionary<ServiceIdObjC *, NSError *> *sendErrorPerRecipientCopy;
|
||||
@synchronized(sendErrors) {
|
||||
sendErrorsCopy = [sendErrors copy];
|
||||
sendErrorPerRecipientCopy = [sendErrorPerRecipient copy];
|
||||
@ -795,8 +796,8 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
|
||||
// Record the individual error for each "failed" recipient.
|
||||
DatabaseStorageWrite(self.databaseStorage, ^(SDSAnyWriteTransaction *transaction) {
|
||||
for (SignalServiceAddress *address in sendErrorPerRecipientCopy) {
|
||||
NSError *error = sendErrorPerRecipientCopy[address];
|
||||
for (ServiceIdObjC *serviceId in sendErrorPerRecipientCopy) {
|
||||
NSError *error = sendErrorPerRecipientCopy[serviceId];
|
||||
|
||||
// Some errors should be ignored when sending messages
|
||||
// to threads other than TSContactThread. See discussion on
|
||||
@ -805,6 +806,7 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
continue;
|
||||
}
|
||||
|
||||
SignalServiceAddress *address = [[SignalServiceAddress alloc] initWithServiceIdObjC:serviceId];
|
||||
[message updateWithFailedRecipient:address error:error transaction:transaction];
|
||||
}
|
||||
});
|
||||
@ -920,12 +922,12 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
DeviceMessage *deviceMessage =
|
||||
[self buildDeviceMessageForMessagePlaintextContent:messageSend.plaintextContent
|
||||
messageEncryptionStyle:messageSend.message.encryptionStyle
|
||||
recipientAddress:recipient.address
|
||||
recipientAccountId:recipient.accountId
|
||||
recipientDeviceId:deviceId
|
||||
recipientId:recipient.accountId
|
||||
serviceId:messageSend.serviceId
|
||||
deviceId:deviceId
|
||||
isOnlineMessage:messageSend.message.isOnline
|
||||
isTransientSenderKeyDistributionMessage:messageSend.message.isTransientSKDM
|
||||
isStorySendMessage:messageSend.message.isStorySend
|
||||
isStoryMessage:messageSend.message.isStorySend
|
||||
isResendRequestMessage:messageSend.message.isResendRequest
|
||||
udSendingParamsProvider:messageSend
|
||||
error:errorHandle];
|
||||
@ -944,12 +946,12 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
|
||||
- (nullable DeviceMessage *)buildDeviceMessageForMessagePlaintextContent:(nullable NSData *)messagePlaintextContent
|
||||
messageEncryptionStyle:(EncryptionStyle)messageEncryptionStyle
|
||||
recipientAddress:(SignalServiceAddress *)recipientAddress
|
||||
recipientAccountId:(NSString *)recipientAccountId
|
||||
recipientDeviceId:(NSNumber *)recipientDeviceId
|
||||
recipientId:(NSString *)recipientId
|
||||
serviceId:(ServiceIdObjC *)serviceId
|
||||
deviceId:(NSNumber *)deviceId
|
||||
isOnlineMessage:(BOOL)isOnlineMessage
|
||||
isTransientSenderKeyDistributionMessage:(BOOL)isTransientSenderKeyDistributionMessage
|
||||
isStorySendMessage:(BOOL)isStorySendMessage
|
||||
isStoryMessage:(BOOL)isStoryMessage
|
||||
isResendRequestMessage:(BOOL)isResendRequestMessage
|
||||
udSendingParamsProvider:
|
||||
(nullable id<UDSendingParamsProvider>)udSendingParamsProvider
|
||||
@ -962,12 +964,12 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
@try {
|
||||
deviceMessage = [self throws_deviceMessageForMessagePlaintextContent:messagePlaintextContent
|
||||
messageEncryptionStyle:messageEncryptionStyle
|
||||
recipientAddress:recipientAddress
|
||||
recipientAccountId:recipientAccountId
|
||||
recipientDeviceId:recipientDeviceId
|
||||
recipientId:recipientId
|
||||
serviceId:serviceId
|
||||
deviceId:deviceId
|
||||
isOnlineMessage:isOnlineMessage
|
||||
isTransientSenderKeyDistributionMessage:isTransientSenderKeyDistributionMessage
|
||||
isStorySendMessage:isStorySendMessage
|
||||
isStoryMessage:isStoryMessage
|
||||
isResendRequestMessage:isResendRequestMessage
|
||||
udSendingParamsProvider:udSendingParamsProvider];
|
||||
} @catch (NSException *exception) {
|
||||
@ -976,10 +978,11 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
if ([exception.name isEqualToString:MessageSenderInvalidDeviceException]) {
|
||||
// If we have an invalid device exception, remove this device from
|
||||
// the recipient and suppress the error.
|
||||
SignalServiceAddress *recipientAddress = [[SignalServiceAddress alloc] initWithServiceIdObjC:serviceId];
|
||||
DatabaseStorageWrite(self.databaseStorage, ^(SDSAnyWriteTransaction *transaction) {
|
||||
[MessageSender updateDevicesWithAddress:recipientAddress
|
||||
devicesToAdd:@[]
|
||||
devicesToRemove:@[ recipientDeviceId ]
|
||||
devicesToRemove:@[ deviceId ]
|
||||
transaction:transaction];
|
||||
});
|
||||
} else if ([exception.name isEqualToString:NoSessionForTransientMessageException]) {
|
||||
@ -994,6 +997,7 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
// she can pull down his latest identity.
|
||||
// If it's happening a lot, we should rethink our profile fetching strategy.
|
||||
OWSProdInfo([OWSAnalyticsEvents messageSendErrorFailedDueToUntrustedKey]);
|
||||
SignalServiceAddress *recipientAddress = [[SignalServiceAddress alloc] initWithServiceIdObjC:serviceId];
|
||||
error = [UntrustedIdentityError asNSErrorWithAddress:recipientAddress];
|
||||
} else if ([exception.name isEqualToString:MessageSenderRateLimitedException]) {
|
||||
error = [SignalServiceRateLimitedError asNSError];
|
||||
@ -1042,11 +1046,10 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
OWSAssertDebug(messageSend.thread);
|
||||
|
||||
TSOutgoingMessage *message = messageSend.message;
|
||||
SignalServiceAddress *address = messageSend.address;
|
||||
OWSAssertDebug(address.isValid);
|
||||
ServiceIdObjC *serviceId = messageSend.serviceId;
|
||||
|
||||
OWSLogInfo(
|
||||
@"attempting to send message: %@, timestamp: %llu, recipient: %@", message.class, message.timestamp, address);
|
||||
@"attempting to send message: %@, timestamp: %llu, recipient: %@", message.class, message.timestamp, serviceId);
|
||||
|
||||
if (messageSend.remainingAttempts <= 0) {
|
||||
// We should always fail with a specific error.
|
||||
@ -1056,14 +1059,6 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
return messageSend.failure(error);
|
||||
}
|
||||
|
||||
// A prior CDS lookup would've resolved the UUID for this recipient if it was registered
|
||||
// If we have no UUID, consider the recipient unregistered.
|
||||
ServiceIdObjC *serviceId = address.serviceIdObjC;
|
||||
if (serviceId == nil) {
|
||||
[self failSendForUnregisteredRecipient:messageSend];
|
||||
return;
|
||||
}
|
||||
|
||||
// Consume an attempt.
|
||||
messageSend.remainingAttempts = messageSend.remainingAttempts - 1;
|
||||
|
||||
@ -1163,7 +1158,7 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
}
|
||||
}
|
||||
|
||||
[self performMessageSendRequest:messageSend serviceId:serviceId deviceMessages:deviceMessages];
|
||||
[self performMessageSendRequest:messageSend deviceMessages:deviceMessages];
|
||||
}
|
||||
|
||||
- (void)handleMessageSentLocally:(TSOutgoingMessage *)message
|
||||
@ -1243,52 +1238,51 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
|
||||
- (DeviceMessage *)throws_deviceMessageForMessagePlaintextContent:(nullable NSData *)messagePlaintextContent
|
||||
messageEncryptionStyle:(EncryptionStyle)messageEncryptionStyle
|
||||
recipientAddress:(SignalServiceAddress *)recipientAddress
|
||||
recipientAccountId:(NSString *)recipientAccountId
|
||||
recipientDeviceId:(NSNumber *)recipientDeviceId
|
||||
recipientId:(NSString *)recipientId
|
||||
serviceId:(ServiceIdObjC *)serviceId
|
||||
deviceId:(NSNumber *)deviceId
|
||||
isOnlineMessage:(BOOL)isOnlineMessage
|
||||
isTransientSenderKeyDistributionMessage:(BOOL)isTransientSenderKeyDistributionMessage
|
||||
isStorySendMessage:(BOOL)isStorySendMessage
|
||||
isStoryMessage:(BOOL)isStoryMessage
|
||||
isResendRequestMessage:(BOOL)isResendRequestMessage
|
||||
udSendingParamsProvider:
|
||||
(nullable id<UDSendingParamsProvider>)udSendingParamsProvider
|
||||
{
|
||||
OWSAssertDebug(!NSThread.isMainThread);
|
||||
OWSAssertDebug(recipientAddress.isValid);
|
||||
|
||||
if (!messagePlaintextContent) {
|
||||
OWSRaiseException(InvalidMessageException, @"No message proto");
|
||||
}
|
||||
|
||||
// This may involve blocking network requests.
|
||||
[self throws_ensureRecipientHasSessionForRecipientAddress:recipientAddress
|
||||
recipientDeviceId:recipientDeviceId
|
||||
recipientAccountId:recipientAccountId
|
||||
forOnlineMessage:isOnlineMessage
|
||||
forTransientSenderKeyDistributionMessage:isTransientSenderKeyDistributionMessage
|
||||
forStorySendMessage:isStorySendMessage
|
||||
udSendingParamsProvider:udSendingParamsProvider];
|
||||
[self throws_ensureRecipientHasSessionForId:recipientId
|
||||
serviceId:serviceId
|
||||
deviceId:deviceId
|
||||
isOnlineMessage:isOnlineMessage
|
||||
isTransientSenderKeyDistributionMessage:isTransientSenderKeyDistributionMessage
|
||||
isStoryMessage:isStoryMessage
|
||||
udSendingParamsProvider:udSendingParamsProvider];
|
||||
|
||||
__block NSError *encryptionError;
|
||||
__block DeviceMessage *__nullable deviceMessage = nil;
|
||||
DatabaseStorageWrite(self.databaseStorage, ^(SDSAnyWriteTransaction *transaction) {
|
||||
switch (messageEncryptionStyle) {
|
||||
case EncryptionStyleWhisper:
|
||||
deviceMessage = [self encryptedMessageForMessagePlaintextContent:messagePlaintextContent
|
||||
recipientAddress:recipientAddress
|
||||
deviceId:recipientDeviceId.intValue
|
||||
udSendingParamsProvider:udSendingParamsProvider
|
||||
transaction:transaction
|
||||
error:&encryptionError];
|
||||
deviceMessage = [self encryptMessageWithPlaintextContent:messagePlaintextContent
|
||||
serviceId:serviceId
|
||||
deviceId:deviceId.intValue
|
||||
udSendingParamsProvider:udSendingParamsProvider
|
||||
transaction:transaction
|
||||
error:&encryptionError];
|
||||
break;
|
||||
case EncryptionStylePlaintext:
|
||||
deviceMessage = [self wrappedPlaintextMessageForMessagePlaintextContent:messagePlaintextContent
|
||||
isResendRequestMessage:isResendRequestMessage
|
||||
recipientAddress:recipientAddress
|
||||
deviceId:recipientDeviceId.intValue
|
||||
udSendingParamsProvider:udSendingParamsProvider
|
||||
transaction:transaction
|
||||
error:&encryptionError];
|
||||
deviceMessage = [self wrapPlaintextMessageWithPlaintextContent:messagePlaintextContent
|
||||
serviceId:serviceId
|
||||
deviceId:deviceId.intValue
|
||||
isResendRequestMessage:isResendRequestMessage
|
||||
udSendingParamsProvider:udSendingParamsProvider
|
||||
transaction:transaction
|
||||
error:&encryptionError];
|
||||
break;
|
||||
default:
|
||||
encryptionError = OWSErrorMakeAssertionError(@"Unrecognized encryption style");
|
||||
@ -1306,26 +1300,24 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
return deviceMessage;
|
||||
}
|
||||
|
||||
- (void)throws_ensureRecipientHasSessionForRecipientAddress:(SignalServiceAddress *)recipientAddress
|
||||
recipientDeviceId:(NSNumber *)recipientDeviceId
|
||||
recipientAccountId:(NSString *)recipientAccountId
|
||||
forOnlineMessage:(BOOL)forOnlineMessage
|
||||
forTransientSenderKeyDistributionMessage:(BOOL)forTransientSenderKeyDistributionMessage
|
||||
forStorySendMessage:(BOOL)forStorySendMessage
|
||||
udSendingParamsProvider:
|
||||
(nullable id<UDSendingParamsProvider>)udSendingParamsProvider
|
||||
- (void)throws_ensureRecipientHasSessionForId:(NSString *)recipientId
|
||||
serviceId:(ServiceIdObjC *)serviceId
|
||||
deviceId:(NSNumber *)deviceId
|
||||
isOnlineMessage:(BOOL)isOnlineMessage
|
||||
isTransientSenderKeyDistributionMessage:(BOOL)isTransientSenderKeyDistributionMessage
|
||||
isStoryMessage:(BOOL)isStoryMessage
|
||||
udSendingParamsProvider:(nullable id<UDSendingParamsProvider>)udSendingParamsProvider
|
||||
{
|
||||
OWSAssertDebug(!NSThread.isMainThread);
|
||||
OWSAssertDebug(recipientDeviceId);
|
||||
OWSAssertDebug(recipientAccountId);
|
||||
|
||||
OWSAssertDebug(recipientAddress.isValid);
|
||||
OWSAssertDebug(recipientId);
|
||||
OWSAssertDebug(serviceId);
|
||||
OWSAssertDebug(deviceId);
|
||||
|
||||
__block BOOL hasSession;
|
||||
[self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) {
|
||||
SSKSessionStore *sessionStore = [self signalProtocolStoreForIdentity:OWSIdentityACI].sessionStore;
|
||||
hasSession = [sessionStore containsActiveSessionForAccountId:recipientAccountId
|
||||
deviceId:[recipientDeviceId intValue]
|
||||
hasSession = [sessionStore containsActiveSessionForAccountId:recipientId
|
||||
deviceId:[deviceId intValue]
|
||||
transaction:transaction];
|
||||
}];
|
||||
if (hasSession) {
|
||||
@ -1335,12 +1327,12 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
__block dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
||||
__block PreKeyBundle *_Nullable bundle;
|
||||
__block NSException *_Nullable exception;
|
||||
[MessageSender makePrekeyRequestForRecipientAddress:recipientAddress
|
||||
recipientDeviceId:recipientDeviceId
|
||||
recipientAccountId:recipientAccountId
|
||||
forOnlineMessage:forOnlineMessage
|
||||
forTransientSenderKeyDistributionMessage:forTransientSenderKeyDistributionMessage
|
||||
forStorySendMessage:forStorySendMessage
|
||||
[MessageSender makePrekeyRequestWithRecipientId:recipientId
|
||||
serviceId:serviceId
|
||||
deviceId:deviceId
|
||||
isOnlineMessage:isOnlineMessage
|
||||
isTransientSenderKeyDistributionMessage:isTransientSenderKeyDistributionMessage
|
||||
isStoryMessage:isStoryMessage
|
||||
udSendingParamsProvider:udSendingParamsProvider
|
||||
success:^(PreKeyBundle *_Nullable responseBundle) {
|
||||
bundle = responseBundle;
|
||||
@ -1401,12 +1393,12 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
|
||||
__block BOOL success;
|
||||
__block NSError *error;
|
||||
DatabaseStorageWrite(self.databaseStorage, ^(SDSAnyWriteTransaction *transaction) {
|
||||
success = [[self class] createSessionForPreKeyBundle:bundle
|
||||
accountId:recipientAccountId
|
||||
recipientAddress:recipientAddress
|
||||
deviceId:recipientDeviceId
|
||||
transaction:transaction
|
||||
error:&error];
|
||||
success = [[self class] createSessionFor:bundle
|
||||
recipientId:recipientId
|
||||
serviceId:serviceId
|
||||
deviceId:deviceId
|
||||
transaction:transaction
|
||||
error:&error];
|
||||
});
|
||||
if (!success) {
|
||||
// NSUnderlyingErrorKey isn't a normal NSException user info key, but it's appropriate here.
|
||||
|
||||
@ -32,20 +32,14 @@ extension TSOutgoingMessage {
|
||||
// MARK: -
|
||||
|
||||
extension MessageSender {
|
||||
|
||||
private struct SessionStates {
|
||||
let deviceAlreadyHasSession = AtomicUInt(0)
|
||||
let deviceDeviceSessionCreated = AtomicUInt(0)
|
||||
let failure = AtomicUInt(0)
|
||||
}
|
||||
|
||||
class func ensureSessions(forMessageSends messageSends: [OWSMessageSend],
|
||||
ignoreErrors: Bool) -> Promise<Void> {
|
||||
class func ensureSessions(
|
||||
forMessageSends messageSends: [OWSMessageSend],
|
||||
ignoreErrors: Bool
|
||||
) -> Promise<Void> {
|
||||
let promise = firstly(on: DispatchQueue.global()) { () -> Promise<Void> in
|
||||
var promises = [Promise<Void>]()
|
||||
for messageSend in messageSends {
|
||||
promises += self.ensureSessions(forMessageSend: messageSend,
|
||||
ignoreErrors: ignoreErrors)
|
||||
promises += self.ensureSessions(forMessageSend: messageSend, ignoreErrors: ignoreErrors)
|
||||
}
|
||||
if !promises.isEmpty {
|
||||
Logger.info("Prekey fetches: \(promises.count)")
|
||||
@ -60,62 +54,61 @@ extension MessageSender {
|
||||
return promise
|
||||
}
|
||||
|
||||
private class func ensureSessions(forMessageSend messageSend: OWSMessageSend,
|
||||
ignoreErrors: Bool) -> [Promise<Void>] {
|
||||
|
||||
var accountId: AccountId?
|
||||
let deviceIdsWithoutSessions: [UInt32] = databaseStorage.read { transaction in
|
||||
guard let recipient = SignalRecipient.get(
|
||||
private class func ensureSessions(
|
||||
forMessageSend messageSend: OWSMessageSend,
|
||||
ignoreErrors: Bool
|
||||
) -> [Promise<Void>] {
|
||||
let (recipientId, deviceIdsWithoutSessions): (AccountId?, [UInt32]) = databaseStorage.read { transaction in
|
||||
let recipient = SignalRecipient.get(
|
||||
address: messageSend.address,
|
||||
mustHaveDevices: false,
|
||||
transaction: transaction
|
||||
) else {
|
||||
// If there is no existing recipient for this address, try and send to
|
||||
// the primary device so we can see if they are registered.
|
||||
return [OWSDevicePrimaryDeviceId]
|
||||
}
|
||||
)
|
||||
|
||||
accountId = recipient.accountId
|
||||
// If there is no existing recipient for this address, try and send to the
|
||||
// primary device so we can see if they are registered.
|
||||
guard let recipient else {
|
||||
return (nil, [OWSDevicePrimaryDeviceId])
|
||||
}
|
||||
|
||||
var deviceIds = recipient.deviceIds ?? []
|
||||
|
||||
// Filter out the current device, we never need a session for it.
|
||||
// Filter out the current device; we never need a session for it.
|
||||
if messageSend.isLocalAddress {
|
||||
let localDeviceId = tsAccountManager.storedDeviceId(with: transaction)
|
||||
deviceIds = deviceIds.filter { $0 != localDeviceId }
|
||||
}
|
||||
|
||||
let sessionStore = signalProtocolStore(for: .aci).sessionStore
|
||||
return deviceIds.filter { deviceId in
|
||||
return (recipient.accountId, deviceIds.filter { deviceId in
|
||||
!sessionStore.containsActiveSession(
|
||||
forAccountId: recipient.accountId,
|
||||
deviceId: Int32(deviceId),
|
||||
transaction: transaction
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
guard !deviceIdsWithoutSessions.isEmpty else { return [] }
|
||||
|
||||
var promises = [Promise<Void>]()
|
||||
for deviceId in deviceIdsWithoutSessions {
|
||||
|
||||
Logger.verbose("Fetching prekey for: \(messageSend.address), \(deviceId)")
|
||||
Logger.verbose("Fetching prekey for: \(messageSend.serviceId), \(deviceId)")
|
||||
|
||||
let promise: Promise<Void> = firstly(on: DispatchQueue.global()) { () -> Promise<SignalServiceKit.PreKeyBundle> in
|
||||
let (promise, future) = Promise<SignalServiceKit.PreKeyBundle>.pending()
|
||||
|
||||
let forOnlineMessage = messageSend.message.isOnline
|
||||
let forTransientSKDM = messageSend.message.isTransientSKDM
|
||||
let forStorySend = messageSend.message.isStorySend
|
||||
let isOnlineMessage = messageSend.message.isOnline
|
||||
let isTransientSenderKeyDistributionMessage = messageSend.message.isTransientSKDM
|
||||
let isStoryMessage = messageSend.message.isStorySend
|
||||
|
||||
self.makePrekeyRequest(
|
||||
forRecipientAddress: messageSend.address,
|
||||
recipientDeviceId: NSNumber(value: deviceId),
|
||||
recipientAccountId: accountId,
|
||||
forOnlineMessage: forOnlineMessage,
|
||||
forTransientSenderKeyDistributionMessage: forTransientSKDM,
|
||||
forStorySendMessage: forStorySend,
|
||||
recipientId: recipientId,
|
||||
serviceId: messageSend.serviceId,
|
||||
deviceId: NSNumber(value: deviceId),
|
||||
isOnlineMessage: isOnlineMessage,
|
||||
isTransientSenderKeyDistributionMessage: isTransientSenderKeyDistributionMessage,
|
||||
isStoryMessage: isStoryMessage,
|
||||
udSendingParamsProvider: messageSend,
|
||||
success: { preKeyBundle in
|
||||
guard let preKeyBundle = preKeyBundle else {
|
||||
@ -130,9 +123,8 @@ extension MessageSender {
|
||||
return promise
|
||||
}.done(on: DispatchQueue.global()) { (preKeyBundle: SignalServiceKit.PreKeyBundle) -> Void in
|
||||
try self.databaseStorage.write { transaction in
|
||||
// Since we successfully fetched the prekey bundle,
|
||||
// we know this device is registered. We can safely
|
||||
// mark it as such to acquire a stable accountId.
|
||||
// Since we successfully fetched the prekey bundle, we know this device is
|
||||
// registered and can mark it as such to acquire a stable recipientId.
|
||||
let recipient = SignalRecipient.fetchOrCreate(
|
||||
for: messageSend.address,
|
||||
trustLevel: .low,
|
||||
@ -141,9 +133,9 @@ extension MessageSender {
|
||||
recipient.markAsRegistered(deviceId: deviceId, transaction: transaction)
|
||||
|
||||
try self.createSession(
|
||||
forPreKeyBundle: preKeyBundle,
|
||||
accountId: recipient.accountId,
|
||||
recipientAddress: messageSend.address,
|
||||
for: preKeyBundle,
|
||||
recipientId: recipient.accountId,
|
||||
serviceId: messageSend.serviceId,
|
||||
deviceId: NSNumber(value: deviceId),
|
||||
transaction: transaction
|
||||
)
|
||||
@ -152,10 +144,12 @@ extension MessageSender {
|
||||
switch error {
|
||||
case MessageSenderError.missingDevice:
|
||||
self.databaseStorage.write { transaction in
|
||||
MessageSender.updateDevices(address: messageSend.address,
|
||||
devicesToAdd: [],
|
||||
devicesToRemove: [NSNumber(value: deviceId)],
|
||||
transaction: transaction)
|
||||
MessageSender.updateDevices(
|
||||
address: messageSend.address,
|
||||
devicesToAdd: [],
|
||||
devicesToRemove: [NSNumber(value: deviceId)],
|
||||
transaction: transaction
|
||||
)
|
||||
}
|
||||
default:
|
||||
break
|
||||
@ -184,29 +178,24 @@ extension MessageSender {
|
||||
|
||||
@objc
|
||||
public extension MessageSender {
|
||||
|
||||
class func makePrekeyRequest(
|
||||
forRecipientAddress recipientAddress: SignalServiceAddress,
|
||||
recipientDeviceId deviceId: NSNumber,
|
||||
recipientAccountId accountId: AccountId?,
|
||||
forOnlineMessage isOnline: Bool,
|
||||
forTransientSenderKeyDistributionMessage isTransientSKDM: Bool,
|
||||
forStorySendMessage isStorySend: Bool,
|
||||
recipientId: AccountId?,
|
||||
serviceId: ServiceIdObjC,
|
||||
deviceId: NSNumber,
|
||||
isOnlineMessage: Bool,
|
||||
isTransientSenderKeyDistributionMessage: Bool,
|
||||
isStoryMessage: Bool,
|
||||
udSendingParamsProvider: UDSendingParamsProvider?,
|
||||
success: @escaping (SignalServiceKit.PreKeyBundle?) -> Void,
|
||||
failure: @escaping (Error) -> Void
|
||||
) {
|
||||
assert(!Thread.isMainThread)
|
||||
assert(recipientAddress.isValid)
|
||||
|
||||
Logger.info("recipientAddress: \(recipientAddress), deviceId: \(deviceId)")
|
||||
let serviceId = serviceId.wrappedValue
|
||||
|
||||
guard let serviceId = recipientAddress.serviceIdObjC else {
|
||||
owsFailDebug("Skipping prekey request due to missing uuid.")
|
||||
return failure(MessageSenderError.missingDevice)
|
||||
}
|
||||
Logger.info("serviceId: \(serviceId), deviceId: \(deviceId)")
|
||||
|
||||
if deviceRecentlyReportedMissing(serviceId: serviceId.wrappedValue, deviceId: deviceId.uint32Value) {
|
||||
if deviceRecentlyReportedMissing(serviceId: serviceId, deviceId: deviceId.uint32Value) {
|
||||
// We don't want to retry prekey requests if we've recently gotten a "404
|
||||
// missing device" for the same recipient/device. Fail immediately as
|
||||
// though we hit the "404 missing device" error again.
|
||||
@ -214,12 +203,12 @@ public extension MessageSender {
|
||||
return failure(MessageSenderError.missingDevice)
|
||||
}
|
||||
|
||||
// If we've never interacted with this account before, we won't
|
||||
// have an accountId. It's safe to skip the identity key check
|
||||
// in that case, since we don't yet know anything about them yet.
|
||||
if let accountId = accountId {
|
||||
guard isPrekeyIdentityKeySafe(accountId: accountId,
|
||||
recipientAddress: recipientAddress) else {
|
||||
// If we've never interacted with this account before, we won't have a
|
||||
// recipientId. It's safe to skip the identity key check in that case,
|
||||
// since we don't know anything about them yet.
|
||||
if let recipientId {
|
||||
let recipientAddress = SignalServiceAddress(serviceId)
|
||||
guard isPrekeyIdentityKeySafe(accountId: recipientId, recipientAddress: recipientAddress) else {
|
||||
// We don't want to make prekey requests if we can anticipate that
|
||||
// we're going to get an untrusted identity error.
|
||||
Logger.info("Skipping prekey request due to untrusted identity.")
|
||||
@ -227,20 +216,20 @@ public extension MessageSender {
|
||||
}
|
||||
}
|
||||
|
||||
if isOnline || isTransientSKDM {
|
||||
if isOnlineMessage || isTransientSenderKeyDistributionMessage {
|
||||
Logger.info("Skipping prekey request for transient message")
|
||||
return failure(MessageSenderNoSessionForTransientMessageError())
|
||||
}
|
||||
|
||||
// Don't use UD for story preKey fetches, we don't have a valid UD auth key
|
||||
let udAccess = isStorySend ? nil : udSendingParamsProvider?.udSendingAccess?.udAccess
|
||||
let udAccess = isStoryMessage ? nil : udSendingParamsProvider?.udSendingAccess?.udAccess
|
||||
|
||||
let requestMaker = RequestMaker(
|
||||
label: "Prekey Fetch",
|
||||
requestFactoryBlock: { (udAccessKeyForRequest: SMKUDAccessKey?) -> TSRequest? in
|
||||
Logger.verbose("Building prekey request for recipientAddress: \(recipientAddress), deviceId: \(deviceId)")
|
||||
Logger.verbose("Building prekey request for serviceId: \(serviceId), deviceId: \(deviceId)")
|
||||
return OWSRequestFactory.recipientPreKeyRequest(
|
||||
withServiceId: serviceId,
|
||||
withServiceId: ServiceIdObjC(serviceId),
|
||||
deviceId: deviceId.stringValue,
|
||||
udAccessKey: udAccessKeyForRequest
|
||||
)
|
||||
@ -250,7 +239,7 @@ public extension MessageSender {
|
||||
// to this recipient also use basic auth.
|
||||
udSendingParamsProvider?.disableUDAuth()
|
||||
},
|
||||
serviceId: serviceId.wrappedValue,
|
||||
serviceId: serviceId,
|
||||
udAccess: udAccess,
|
||||
authedAccount: .implicit(),
|
||||
// The v2/keys endpoint isn't supported via web sockets, so don't try and
|
||||
@ -269,7 +258,7 @@ public extension MessageSender {
|
||||
}.catch(on: DispatchQueue.global()) { error in
|
||||
if let httpStatusCode = error.httpStatusCode {
|
||||
if httpStatusCode == 404 {
|
||||
self.reportMissingDeviceError(serviceId: serviceId.wrappedValue, deviceId: deviceId.uint32Value)
|
||||
self.reportMissingDeviceError(serviceId: serviceId, deviceId: deviceId.uint32Value)
|
||||
return failure(MessageSenderError.missingDevice)
|
||||
} else if httpStatusCode == 413 || httpStatusCode == 429 {
|
||||
return failure(MessageSenderError.prekeyRateLimit)
|
||||
@ -303,19 +292,28 @@ public extension MessageSender {
|
||||
}
|
||||
}
|
||||
|
||||
@objc(createSessionForPreKeyBundle:accountId:recipientAddress:deviceId:transaction:error:)
|
||||
class func createSession(forPreKeyBundle preKeyBundle: SignalServiceKit.PreKeyBundle,
|
||||
accountId: String,
|
||||
recipientAddress: SignalServiceAddress,
|
||||
deviceId: NSNumber,
|
||||
transaction: SDSAnyWriteTransaction) throws {
|
||||
class func createSession(
|
||||
for preKeyBundle: SignalServiceKit.PreKeyBundle,
|
||||
recipientId: String,
|
||||
serviceId: ServiceIdObjC,
|
||||
deviceId: NSNumber,
|
||||
transaction: SDSAnyWriteTransaction
|
||||
) throws {
|
||||
assert(!Thread.isMainThread)
|
||||
|
||||
let recipientAddress = SignalServiceAddress(serviceIdObjC: serviceId)
|
||||
|
||||
Logger.info("Creating session for recipientAddress: \(recipientAddress), deviceId: \(deviceId)")
|
||||
|
||||
guard !signalProtocolStore(for: .aci).sessionStore.containsActiveSession(forAccountId: accountId,
|
||||
deviceId: deviceId.int32Value,
|
||||
transaction: transaction) else {
|
||||
let containsActiveSession = { () -> Bool in
|
||||
signalProtocolStore(for: .aci).sessionStore.containsActiveSession(
|
||||
forAccountId: recipientId,
|
||||
deviceId: deviceId.int32Value,
|
||||
transaction: transaction
|
||||
)
|
||||
}
|
||||
|
||||
guard !containsActiveSession() else {
|
||||
Logger.warn("Session already exists.")
|
||||
return
|
||||
}
|
||||
@ -342,24 +340,24 @@ public extension MessageSender {
|
||||
}
|
||||
|
||||
do {
|
||||
let protocolAddress = try ProtocolAddress(from: recipientAddress, deviceId: deviceId.uint32Value)
|
||||
try processPreKeyBundle(bundle,
|
||||
for: protocolAddress,
|
||||
sessionStore: signalProtocolStore(for: .aci).sessionStore,
|
||||
identityStore: identityManager.store(for: .aci, transaction: transaction),
|
||||
context: transaction)
|
||||
let protocolAddress = try ProtocolAddress(uuid: serviceId.uuidValue, deviceId: deviceId.uint32Value)
|
||||
try processPreKeyBundle(
|
||||
bundle,
|
||||
for: protocolAddress,
|
||||
sessionStore: signalProtocolStore(for: .aci).sessionStore,
|
||||
identityStore: identityManager.store(for: .aci, transaction: transaction),
|
||||
context: transaction
|
||||
)
|
||||
} catch SignalError.untrustedIdentity(_) {
|
||||
handleUntrustedIdentityKeyError(accountId: accountId,
|
||||
recipientAddress: recipientAddress,
|
||||
preKeyBundle: preKeyBundle,
|
||||
transaction: transaction)
|
||||
handleUntrustedIdentityKeyError(
|
||||
accountId: recipientId,
|
||||
recipientAddress: recipientAddress,
|
||||
preKeyBundle: preKeyBundle,
|
||||
transaction: transaction
|
||||
)
|
||||
throw UntrustedIdentityError(address: recipientAddress)
|
||||
}
|
||||
if !signalProtocolStore(for: .aci).sessionStore.containsActiveSession(forAccountId: accountId,
|
||||
deviceId: deviceId.int32Value,
|
||||
transaction: transaction) {
|
||||
owsFailDebug("Session does not exist.")
|
||||
}
|
||||
owsAssertDebug(containsActiveSession(), "Session does not exist.")
|
||||
}
|
||||
|
||||
class func handleUntrustedIdentityKeyError(accountId: String,
|
||||
@ -540,16 +538,18 @@ public class MessageSendInfo: NSObject {
|
||||
|
||||
// These recipients should be sent to during this cycle of send attempts.
|
||||
@objc
|
||||
public let recipients: [SignalServiceAddress]
|
||||
public let serviceIds: [ServiceIdObjC]
|
||||
|
||||
@objc
|
||||
public let senderCertificates: SenderCertificates
|
||||
|
||||
required init(thread: TSThread,
|
||||
recipients: [SignalServiceAddress],
|
||||
senderCertificates: SenderCertificates) {
|
||||
required init(
|
||||
thread: TSThread,
|
||||
serviceIds: [ServiceId],
|
||||
senderCertificates: SenderCertificates
|
||||
) {
|
||||
self.thread = thread
|
||||
self.recipients = recipients
|
||||
self.serviceIds = serviceIds.map { ServiceIdObjC($0) }
|
||||
self.senderCertificates = senderCertificates
|
||||
}
|
||||
}
|
||||
@ -586,66 +586,64 @@ extension MessageSender {
|
||||
)
|
||||
return promise
|
||||
}.then(on: DispatchQueue.global()) { senderCertificates in
|
||||
self.prepareRecipients(of: message, senderCertificates: senderCertificates)
|
||||
}
|
||||
}
|
||||
|
||||
private static func prepareRecipients(of message: TSOutgoingMessage,
|
||||
senderCertificates: SenderCertificates) -> Promise<MessageSendInfo> {
|
||||
|
||||
firstly(on: DispatchQueue.global()) { () -> MessageSendInfo in
|
||||
guard let localAddress = tsAccountManager.localAddress else {
|
||||
throw OWSAssertionError("Missing localAddress.")
|
||||
}
|
||||
let possibleThread = Self.databaseStorage.read { transaction in
|
||||
TSThread.anyFetch(uniqueId: message.uniqueThreadId, transaction: transaction)
|
||||
}
|
||||
guard let thread = possibleThread else {
|
||||
let thread = self.databaseStorage.read { TSThread.anyFetch(uniqueId: message.uniqueThreadId, transaction: $0) }
|
||||
guard let thread else {
|
||||
Logger.warn("Skipping send due to missing thread.")
|
||||
throw MessageSenderError.threadMissing
|
||||
}
|
||||
|
||||
if message.isSyncMessage {
|
||||
// Sync messages are just sent to the local user.
|
||||
return MessageSendInfo(thread: thread,
|
||||
recipients: [localAddress],
|
||||
senderCertificates: senderCertificates)
|
||||
return try self.prepareRecipients(of: message, thread: thread).map(on: DispatchQueue.global()) { preparedRecipients in
|
||||
return MessageSendInfo(
|
||||
thread: thread,
|
||||
serviceIds: preparedRecipients,
|
||||
senderCertificates: senderCertificates
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let proposedRecipients = try self.unsentRecipients(of: message, thread: thread)
|
||||
return MessageSendInfo(thread: thread,
|
||||
recipients: proposedRecipients,
|
||||
senderCertificates: senderCertificates)
|
||||
}.then(on: DispatchQueue.global()) { (sendInfo: MessageSendInfo) -> Promise<MessageSendInfo> in
|
||||
private static func prepareRecipients(of message: TSOutgoingMessage, thread: TSThread) throws -> Promise<[ServiceId]> {
|
||||
guard let localAddress = tsAccountManager.localAddress else {
|
||||
throw OWSAssertionError("Missing localAddress.")
|
||||
}
|
||||
|
||||
// Figure out which addresses we expect to receive the message.
|
||||
let proposedRecipients: [SignalServiceAddress]
|
||||
if message.isSyncMessage {
|
||||
// Sync messages are just sent to the local user.
|
||||
proposedRecipients = [localAddress]
|
||||
} else {
|
||||
proposedRecipients = try self.unsentRecipients(of: message, thread: thread)
|
||||
}
|
||||
|
||||
return firstly(on: DispatchQueue.global()) { () -> Promise<[ServiceId]> in
|
||||
// We might need to use CDS to fill in missing UUIDs and/or identify
|
||||
// which recipients are unregistered.
|
||||
return firstly(on: DispatchQueue.global()) { () -> Promise<[SignalServiceAddress]> in
|
||||
Self.ensureRecipientAddresses(sendInfo.recipients, message: message)
|
||||
}.map(on: DispatchQueue.global()) { (registeredRecipients: [SignalServiceAddress]) in
|
||||
// For group story replies, we must check if the recipients are stories capable
|
||||
guard message.isGroupStoryReply else { return registeredRecipients }
|
||||
return fetchServiceIds(for: proposedRecipients)
|
||||
|
||||
let profiles = databaseStorage.read {
|
||||
Self.profileManager.getUserProfiles(forAddresses: registeredRecipients, transaction: $0)
|
||||
}.map(on: DispatchQueue.global()) { (registeredRecipients: [ServiceId]) in
|
||||
var filteredRecipients = registeredRecipients
|
||||
|
||||
// For group story replies, we must check if the recipients are stories capable
|
||||
if message.isGroupStoryReply {
|
||||
let userProfiles = databaseStorage.read {
|
||||
Self.profileManager.getUserProfiles(
|
||||
forAddresses: registeredRecipients.map { SignalServiceAddress($0) },
|
||||
transaction: $0
|
||||
)
|
||||
}
|
||||
filteredRecipients = filteredRecipients.filter {
|
||||
userProfiles[SignalServiceAddress($0)]?.isStoriesCapable == true
|
||||
}
|
||||
|
||||
return registeredRecipients.filter { profiles[$0]?.isStoriesCapable == true }
|
||||
}.map(on: DispatchQueue.global()) { (validRecipients: [SignalServiceAddress]) in
|
||||
// Replace recipients with validRecipients.
|
||||
MessageSendInfo(thread: sendInfo.thread,
|
||||
recipients: validRecipients,
|
||||
senderCertificates: sendInfo.senderCertificates)
|
||||
}
|
||||
}.map(on: DispatchQueue.global()) { (sendInfo: MessageSendInfo) -> MessageSendInfo in
|
||||
// Mark skipped recipients as such. We skip because:
|
||||
|
||||
// Mark skipped recipients as such. We may skip because:
|
||||
//
|
||||
// * Recipient is no longer in the group.
|
||||
// * Recipient is blocked.
|
||||
// * Recipient is unregistered.
|
||||
// * Recipient does not have the required capability.
|
||||
//
|
||||
// Elsewhere, we skip recipient if their Signal account has been deactivated.
|
||||
let skippedRecipients = Set(message.sendingRecipientAddresses()).subtracting(sendInfo.recipients)
|
||||
// * A recipient is no longer in the group.
|
||||
// * A recipient is blocked.
|
||||
// * A recipient is unregistered.
|
||||
// * A recipient does not have the required capability.
|
||||
let skippedRecipients = Set(message.sendingRecipientAddresses())
|
||||
.subtracting(filteredRecipients.lazy.map { SignalServiceAddress($0) })
|
||||
if !skippedRecipients.isEmpty {
|
||||
self.databaseStorage.write { transaction in
|
||||
for address in skippedRecipients {
|
||||
@ -655,7 +653,7 @@ extension MessageSender {
|
||||
}
|
||||
}
|
||||
|
||||
return sendInfo
|
||||
return filteredRecipients
|
||||
}
|
||||
}
|
||||
|
||||
@ -750,28 +748,38 @@ extension MessageSender {
|
||||
}
|
||||
}
|
||||
|
||||
private static func ensureRecipientAddresses(_ addresses: [SignalServiceAddress],
|
||||
message: TSOutgoingMessage) -> Promise<[SignalServiceAddress]> {
|
||||
private static func fetchServiceIds(for addresses: [SignalServiceAddress]) -> Promise<[ServiceId]> {
|
||||
var serviceIds = [ServiceId]()
|
||||
var phoneNumbersToFetch = [E164]()
|
||||
|
||||
let invalidRecipients = addresses.filter { $0.uuid == nil }
|
||||
guard !invalidRecipients.isEmpty else {
|
||||
// All recipients are already valid.
|
||||
return Promise.value(addresses)
|
||||
for address in addresses {
|
||||
if let serviceId = address.serviceId {
|
||||
serviceIds.append(serviceId)
|
||||
} else if let phoneNumber = address.e164 {
|
||||
phoneNumbersToFetch.append(phoneNumber)
|
||||
} else {
|
||||
owsFailDebug("Recipient has neither ServiceId nor E164.")
|
||||
}
|
||||
}
|
||||
|
||||
let phoneNumbersToFetch = invalidRecipients.compactMap { $0.phoneNumber }
|
||||
owsAssertDebug(!phoneNumbersToFetch.isEmpty, "Invalid recipients have neither phone number nor UUID.")
|
||||
// Check if all recipients are already valid.
|
||||
if phoneNumbersToFetch.isEmpty {
|
||||
return .value(serviceIds)
|
||||
}
|
||||
|
||||
// If not, look up ServiceIds for the phone numbers that don't have them.
|
||||
return firstly { () -> Promise<Set<SignalRecipient>> in
|
||||
contactDiscoveryManager.lookUp(phoneNumbers: Set(phoneNumbersToFetch), mode: .outgoingMessage)
|
||||
}.map(on: DispatchQueue.sharedUtility) { (signalRecipients: Set<SignalRecipient>) -> [SignalServiceAddress] in
|
||||
contactDiscoveryManager.lookUp(
|
||||
phoneNumbers: Set(phoneNumbersToFetch.lazy.map { $0.stringValue }),
|
||||
mode: .outgoingMessage
|
||||
)
|
||||
}.map(on: DispatchQueue.sharedUtility) { (signalRecipients: Set<SignalRecipient>) -> [ServiceId] in
|
||||
for signalRecipient in signalRecipients {
|
||||
owsAssertDebug(signalRecipient.address.phoneNumber != nil)
|
||||
owsAssertDebug(signalRecipient.address.uuid != nil)
|
||||
}
|
||||
var validRecipients = Set(addresses).subtracting(invalidRecipients)
|
||||
validRecipients.formUnion(signalRecipients.compactMap { $0.address })
|
||||
return Array(validRecipients)
|
||||
serviceIds.append(contentsOf: signalRecipients.lazy.compactMap { ServiceId(uuidString: $0.recipientUUID) })
|
||||
return serviceIds
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -803,7 +811,6 @@ extension MessageSender {
|
||||
|
||||
func performMessageSendRequest(
|
||||
_ messageSend: OWSMessageSend,
|
||||
serviceId: ServiceIdObjC,
|
||||
deviceMessages: [DeviceMessage]
|
||||
) {
|
||||
owsAssertDebug(!Thread.isMainThread)
|
||||
@ -831,7 +838,7 @@ extension MessageSender {
|
||||
label: "Message Send",
|
||||
requestFactoryBlock: { (udAccessKey: SMKUDAccessKey?) in
|
||||
OWSRequestFactory.submitMessageRequest(
|
||||
withServiceId: serviceId,
|
||||
withServiceId: messageSend.serviceId,
|
||||
messages: deviceMessages,
|
||||
timestamp: message.timestamp,
|
||||
udAccessKey: udAccessKey,
|
||||
@ -845,7 +852,7 @@ extension MessageSender {
|
||||
// to this recipient also use basic auth.
|
||||
messageSend.disableUDAuth()
|
||||
},
|
||||
serviceId: serviceId.wrappedValue,
|
||||
serviceId: messageSend.serviceId.wrappedValue,
|
||||
udAccess: messageSend.udSendingAccess?.udAccess,
|
||||
authedAccount: .implicit(),
|
||||
options: []
|
||||
@ -907,20 +914,17 @@ extension MessageSender {
|
||||
|
||||
Self.databaseStorage.write { transaction in
|
||||
deviceMessages.forEach { deviceMessage in
|
||||
guard let uuid = address.uuid else {
|
||||
return owsFailDebug("Expected a destination")
|
||||
}
|
||||
|
||||
if let payloadId = messageSend.plaintextPayloadId {
|
||||
MessageSendLog.recordPendingDelivery(
|
||||
payloadId: payloadId,
|
||||
recipientUuid: uuid,
|
||||
recipientUuid: messageSend.serviceId.uuidValue,
|
||||
recipientDeviceId: Int64(deviceMessage.destinationDeviceId),
|
||||
message: message,
|
||||
transaction: transaction)
|
||||
}
|
||||
}
|
||||
message.update(withSentRecipient: address, wasSentByUD: wasSentByUD, transaction: transaction)
|
||||
|
||||
message.update(withSentRecipient: messageSend.serviceId, wasSentByUD: wasSentByUD, transaction: transaction)
|
||||
|
||||
transaction.addSyncCompletion {
|
||||
BenchManager.completeEvent(eventId: "sendMessageNetwork-\(message.timestamp)")
|
||||
@ -1070,14 +1074,18 @@ extension MessageSender {
|
||||
func failSendForUnregisteredRecipient(_ messageSend: OWSMessageSend) {
|
||||
owsAssertDebug(!Thread.isMainThread)
|
||||
|
||||
let address: SignalServiceAddress = messageSend.address
|
||||
let message: TSOutgoingMessage = messageSend.message
|
||||
|
||||
Logger.verbose("Unregistered recipient: \(address)")
|
||||
Logger.verbose("Unregistered recipient: \(messageSend.serviceId)")
|
||||
|
||||
if !message.isSyncMessage {
|
||||
databaseStorage.write { writeTx in
|
||||
markAddressAsUnregistered(address, message: message, thread: messageSend.thread, transaction: writeTx)
|
||||
markAsUnregistered(
|
||||
serviceId: messageSend.serviceId.wrappedValue,
|
||||
message: message,
|
||||
thread: messageSend.thread,
|
||||
transaction: writeTx
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1085,12 +1093,17 @@ extension MessageSender {
|
||||
messageSend.failure(error)
|
||||
}
|
||||
|
||||
func markAddressAsUnregistered(_ address: SignalServiceAddress,
|
||||
message: TSOutgoingMessage,
|
||||
thread: TSThread,
|
||||
transaction: SDSAnyWriteTransaction) {
|
||||
@nonobjc
|
||||
func markAsUnregistered(
|
||||
serviceId: ServiceId,
|
||||
message: TSOutgoingMessage,
|
||||
thread: TSThread,
|
||||
transaction: SDSAnyWriteTransaction
|
||||
) {
|
||||
owsAssertDebug(!Thread.isMainThread)
|
||||
|
||||
let address = SignalServiceAddress(serviceId)
|
||||
|
||||
if thread.isNonContactThread {
|
||||
// Mark as "skipped" group members who no longer have signal accounts.
|
||||
message.update(withSkippedRecipient: address, transaction: transaction)
|
||||
@ -1186,23 +1199,27 @@ extension MessageSender {
|
||||
|
||||
extension MessageSender {
|
||||
|
||||
@objc(encryptedMessageForMessagePlaintextContent:recipientAddress:deviceId:udSendingParamsProvider:transaction:error:)
|
||||
func encryptedMessage(
|
||||
messagePlaintextContent plaintextContent: Data?,
|
||||
recipientAddress: SignalServiceAddress,
|
||||
@objc
|
||||
func encryptMessage(
|
||||
plaintextContent: Data?,
|
||||
serviceId: ServiceIdObjC,
|
||||
deviceId: Int32,
|
||||
udSendingParamsProvider: UDSendingParamsProvider?,
|
||||
transaction: SDSAnyWriteTransaction
|
||||
) throws -> DeviceMessage {
|
||||
owsAssertDebug(!Thread.isMainThread)
|
||||
|
||||
owsAssertDebug(recipientAddress.isValid)
|
||||
let serviceId = serviceId.wrappedValue
|
||||
|
||||
let signalProtocolStore = signalProtocolStore(for: .aci)
|
||||
guard signalProtocolStore.sessionStore.containsActiveSession(for: recipientAddress,
|
||||
deviceId: deviceId,
|
||||
transaction: transaction) else {
|
||||
throw MessageSendEncryptionError(recipientAddress: recipientAddress, deviceId: deviceId)
|
||||
guard
|
||||
signalProtocolStore.sessionStore.containsActiveSession(
|
||||
for: serviceId,
|
||||
deviceId: deviceId,
|
||||
transaction: transaction
|
||||
)
|
||||
else {
|
||||
throw MessageSendEncryptionError(recipientAddress: SignalServiceAddress(serviceId), deviceId: deviceId)
|
||||
}
|
||||
guard let plainText = plaintextContent else {
|
||||
throw OWSAssertionError("Missing message content")
|
||||
@ -1213,36 +1230,37 @@ extension MessageSender {
|
||||
let serializedMessage: Data
|
||||
let messageType: SSKProtoEnvelopeType
|
||||
|
||||
let protocolAddress = try ProtocolAddress(from: recipientAddress, deviceId: UInt32(bitPattern: deviceId))
|
||||
let protocolAddress = try ProtocolAddress(uuid: serviceId.uuidValue, deviceId: UInt32(bitPattern: deviceId))
|
||||
|
||||
if
|
||||
let udSendingParamsProvider,
|
||||
let udSendingAccess = udSendingParamsProvider.udSendingAccess
|
||||
{
|
||||
if let udSendingParamsProvider, let udSendingAccess = udSendingParamsProvider.udSendingAccess {
|
||||
let secretCipher = try SMKSecretSessionCipher(
|
||||
sessionStore: signalProtocolStore.sessionStore,
|
||||
preKeyStore: signalProtocolStore.preKeyStore,
|
||||
signedPreKeyStore: signalProtocolStore.signedPreKeyStore,
|
||||
identityStore: identityManager.store(for: .aci, transaction: transaction),
|
||||
senderKeyStore: Self.senderKeyStore)
|
||||
senderKeyStore: Self.senderKeyStore
|
||||
)
|
||||
|
||||
serializedMessage = try secretCipher.encryptMessage(
|
||||
recipient: recipientAddress,
|
||||
for: serviceId,
|
||||
deviceId: deviceId,
|
||||
paddedPlaintext: paddedPlaintext,
|
||||
contentHint: udSendingParamsProvider.contentHint.signalClientHint,
|
||||
groupId: udSendingParamsProvider.envelopeGroupId(transaction: transaction),
|
||||
senderCertificate: udSendingAccess.senderCertificate,
|
||||
protocolContext: transaction)
|
||||
protocolContext: transaction
|
||||
)
|
||||
|
||||
messageType = .unidentifiedSender
|
||||
|
||||
} else {
|
||||
let result = try signalEncrypt(message: paddedPlaintext,
|
||||
for: protocolAddress,
|
||||
sessionStore: signalProtocolStore.sessionStore,
|
||||
identityStore: identityManager.store(for: .aci, transaction: transaction),
|
||||
context: transaction)
|
||||
let result = try signalEncrypt(
|
||||
message: paddedPlaintext,
|
||||
for: protocolAddress,
|
||||
sessionStore: signalProtocolStore.sessionStore,
|
||||
identityStore: identityManager.store(for: .aci, transaction: transaction),
|
||||
context: transaction
|
||||
)
|
||||
|
||||
switch result.messageType {
|
||||
case .whisper:
|
||||
@ -1277,19 +1295,19 @@ extension MessageSender {
|
||||
)
|
||||
}
|
||||
|
||||
@objc(wrappedPlaintextMessageForMessagePlaintextContent:isResendRequestMessage:recipientAddress:deviceId:udSendingParamsProvider:transaction:error:)
|
||||
func wrappedPlaintextMessage(
|
||||
messagePlaintextContent plaintextContent: Data?,
|
||||
isResendRequestMessage: Bool,
|
||||
recipientAddress: SignalServiceAddress,
|
||||
@objc
|
||||
func wrapPlaintextMessage(
|
||||
plaintextContent: Data?,
|
||||
serviceId: ServiceIdObjC,
|
||||
deviceId: Int32,
|
||||
isResendRequestMessage: Bool,
|
||||
udSendingParamsProvider: UDSendingParamsProvider?,
|
||||
transaction: SDSAnyWriteTransaction
|
||||
) throws -> DeviceMessage {
|
||||
owsAssertDebug(!Thread.isMainThread)
|
||||
|
||||
guard recipientAddress.isValid else { throw OWSAssertionError("Invalid address") }
|
||||
let protocolAddress = try ProtocolAddress(from: recipientAddress, deviceId: UInt32(bitPattern: deviceId))
|
||||
let serviceId = serviceId.wrappedValue
|
||||
let protocolAddress = try ProtocolAddress(uuid: serviceId.uuidValue, deviceId: UInt32(bitPattern: deviceId))
|
||||
|
||||
// Only resend request messages are allowed to use this codepath.
|
||||
guard isResendRequestMessage else {
|
||||
@ -1302,10 +1320,7 @@ extension MessageSender {
|
||||
let serializedMessage: Data
|
||||
let messageType: SSKProtoEnvelopeType
|
||||
|
||||
if
|
||||
let udSendingParamsProvider,
|
||||
let udSendingAccess = udSendingParamsProvider.udSendingAccess
|
||||
{
|
||||
if let udSendingParamsProvider, let udSendingAccess = udSendingParamsProvider.udSendingAccess {
|
||||
let usmc = try UnidentifiedSenderMessageContent(
|
||||
CiphertextMessage(plaintext),
|
||||
from: udSendingAccess.senderCertificate,
|
||||
@ -1316,7 +1331,8 @@ extension MessageSender {
|
||||
usmc,
|
||||
for: protocolAddress,
|
||||
identityStore: identityManager.store(for: .aci, transaction: transaction),
|
||||
context: transaction)
|
||||
context: transaction
|
||||
)
|
||||
|
||||
serializedMessage = Data(outerBytes)
|
||||
messageType = .unidentifiedSender
|
||||
|
||||
@ -280,10 +280,15 @@ extension OWSIdentityManager {
|
||||
|
||||
@objc(shouldSharePhoneNumberWithAddress:transaction:)
|
||||
func shouldSharePhoneNumber(with recipient: SignalServiceAddress, transaction: SDSAnyReadTransaction) -> Bool {
|
||||
guard let recipientUuid = recipient.uuidString else {
|
||||
guard let serviceId = recipient.serviceId else {
|
||||
return false
|
||||
}
|
||||
return shareMyPhoneNumberStore.getBool(recipientUuid, defaultValue: false, transaction: transaction)
|
||||
return shouldSharePhoneNumber(with: serviceId, transaction: transaction)
|
||||
}
|
||||
|
||||
func shouldSharePhoneNumber(with serviceId: ServiceId, transaction: SDSAnyReadTransaction) -> Bool {
|
||||
let uuidString = serviceId.uuidValue.uuidString
|
||||
return shareMyPhoneNumberStore.getBool(uuidString, defaultValue: false, transaction: transaction)
|
||||
}
|
||||
|
||||
func setShouldSharePhoneNumber(with recipient: SignalServiceAddress, transaction: SDSAnyWriteTransaction) {
|
||||
|
||||
@ -43,6 +43,9 @@ public class OWSMessageSend: NSObject, UDSendingParamsProvider {
|
||||
@objc
|
||||
public let thread: TSThread
|
||||
|
||||
@objc
|
||||
public let serviceId: ServiceIdObjC
|
||||
|
||||
@objc
|
||||
public let address: SignalServiceAddress
|
||||
|
||||
@ -79,7 +82,7 @@ public class OWSMessageSend: NSObject, UDSendingParamsProvider {
|
||||
plaintextContent: Data?,
|
||||
plaintextPayloadId: NSNumber?,
|
||||
thread: TSThread,
|
||||
address: SignalServiceAddress,
|
||||
serviceId: ServiceIdObjC,
|
||||
udSendingAccess: OWSUDSendingAccess?,
|
||||
localAddress: SignalServiceAddress,
|
||||
sendErrorBlock: ((Error) -> Void)?) {
|
||||
@ -87,7 +90,8 @@ public class OWSMessageSend: NSObject, UDSendingParamsProvider {
|
||||
self.plaintextContent = plaintextContent
|
||||
self.plaintextPayloadId = plaintextPayloadId?.int64Value
|
||||
self.thread = thread
|
||||
self.address = address
|
||||
self.serviceId = serviceId
|
||||
self.address = SignalServiceAddress(serviceId.wrappedValue)
|
||||
self.localAddress = localAddress
|
||||
self.isLocalAddress = address.isLocalAddress
|
||||
|
||||
|
||||
@ -118,11 +118,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateWithSentRecipient:(SignalServiceAddress *)recipientAddress
|
||||
- (void)updateWithSentRecipient:(ServiceIdObjC *)serviceId
|
||||
wasSentByUD:(BOOL)wasSentByUD
|
||||
transaction:(SDSAnyWriteTransaction *)transaction
|
||||
{
|
||||
[super updateWithSentRecipient:recipientAddress wasSentByUD:wasSentByUD transaction:transaction];
|
||||
[super updateWithSentRecipient:serviceId wasSentByUD:wasSentByUD transaction:transaction];
|
||||
|
||||
// Message was sent! Re-mark the recipient as having been sent an SKDM
|
||||
if (self.didAppendSKDM) {
|
||||
@ -133,7 +133,7 @@
|
||||
if (originalThread.usesSenderKey) {
|
||||
NSError *error = nil;
|
||||
[self.senderKeyStore recordSenderKeySentFor:originalThread
|
||||
to:recipientAddress
|
||||
to:serviceId
|
||||
timestamp:self.timestamp
|
||||
writeTx:transaction
|
||||
error:&error];
|
||||
|
||||
@ -150,12 +150,14 @@ public protocol OWSUDManager: AnyObject {
|
||||
func udAccess(forAddress address: SignalServiceAddress, requireSyncAccess: Bool) -> OWSUDAccess?
|
||||
|
||||
@objc
|
||||
func udSendingAccess(forAddress address: SignalServiceAddress,
|
||||
requireSyncAccess: Bool,
|
||||
senderCertificates: SenderCertificates) -> OWSUDSendingAccess?
|
||||
func udSendingAccess(
|
||||
for serviceId: ServiceIdObjC,
|
||||
requireSyncAccess: Bool,
|
||||
senderCertificates: SenderCertificates
|
||||
) -> OWSUDSendingAccess?
|
||||
|
||||
@objc
|
||||
func storySendingAccess(forAddress address: SignalServiceAddress, senderCertificates: SenderCertificates) -> OWSUDSendingAccess
|
||||
func storySendingAccess(for serviceId: ServiceIdObjC, senderCertificates: SenderCertificates) -> OWSUDSendingAccess
|
||||
|
||||
// MARK: Sender Certificate
|
||||
|
||||
@ -518,36 +520,43 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
|
||||
|
||||
// Returns the UD access key and appropriate sender certificate for sending to a given recipient
|
||||
@objc
|
||||
public func udSendingAccess(forAddress address: SignalServiceAddress,
|
||||
requireSyncAccess: Bool,
|
||||
senderCertificates: SenderCertificates) -> OWSUDSendingAccess? {
|
||||
public func udSendingAccess(
|
||||
for serviceId: ServiceIdObjC,
|
||||
requireSyncAccess: Bool,
|
||||
senderCertificates: SenderCertificates
|
||||
) -> OWSUDSendingAccess? {
|
||||
let address = SignalServiceAddress(serviceId.wrappedValue)
|
||||
guard let udAccess = self.udAccess(forAddress: address, requireSyncAccess: requireSyncAccess) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return udSendingAccess(for: address, udAccess: udAccess, senderCertificates: senderCertificates)
|
||||
return udSendingAccess(for: serviceId.wrappedValue, udAccess: udAccess, senderCertificates: senderCertificates)
|
||||
}
|
||||
|
||||
@objc
|
||||
public func storySendingAccess(forAddress address: SignalServiceAddress, senderCertificates: SenderCertificates) -> OWSUDSendingAccess {
|
||||
public func storySendingAccess(for serviceId: ServiceIdObjC, senderCertificates: SenderCertificates) -> OWSUDSendingAccess {
|
||||
let udAccess = OWSUDAccess(udAccessKey: randomUDAccessKey(), udAccessMode: .unrestricted, isRandomKey: true)
|
||||
return udSendingAccess(for: address, udAccess: udAccess, senderCertificates: senderCertificates)
|
||||
return udSendingAccess(for: serviceId.wrappedValue, udAccess: udAccess, senderCertificates: senderCertificates)
|
||||
}
|
||||
|
||||
private func udSendingAccess(for address: SignalServiceAddress, udAccess: OWSUDAccess, senderCertificates: SenderCertificates) -> OWSUDSendingAccess {
|
||||
private func udSendingAccess(
|
||||
for serviceId: ServiceId,
|
||||
udAccess: OWSUDAccess,
|
||||
senderCertificates: SenderCertificates
|
||||
) -> OWSUDSendingAccess {
|
||||
databaseStorage.read { transaction in
|
||||
let senderCertificate: SenderCertificate
|
||||
switch phoneNumberSharingMode {
|
||||
case .everybody:
|
||||
senderCertificate = senderCertificates.defaultCert
|
||||
case .contactsOnly:
|
||||
let address = SignalServiceAddress(serviceId)
|
||||
if Self.contactsManager.isSystemContact(address: address, transaction: transaction) {
|
||||
senderCertificate = senderCertificates.defaultCert
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case .nobody:
|
||||
if identityManager.shouldSharePhoneNumber(with: address, transaction: transaction) {
|
||||
if identityManager.shouldSharePhoneNumber(with: serviceId, transaction: transaction) {
|
||||
senderCertificate = senderCertificates.defaultCert
|
||||
break
|
||||
}
|
||||
|
||||
@ -143,7 +143,7 @@ public class SMKSecretSessionCipher: NSObject {
|
||||
// MARK: - Public
|
||||
|
||||
public func encryptMessage(
|
||||
recipient: SignalServiceAddress,
|
||||
for serviceId: ServiceId,
|
||||
deviceId: Int32,
|
||||
paddedPlaintext: Data,
|
||||
contentHint: UnidentifiedSenderMessageContent.ContentHint,
|
||||
@ -155,7 +155,7 @@ public class SMKSecretSessionCipher: NSObject {
|
||||
guard deviceId > 0 else {
|
||||
throw SMKError.assertionError(description: "\(logTag) invalid deviceId")
|
||||
}
|
||||
let recipientAddress = try ProtocolAddress(from: recipient, deviceId: UInt32(bitPattern: deviceId))
|
||||
let recipientAddress = try ProtocolAddress(uuid: serviceId.uuidValue, deviceId: UInt32(bitPattern: deviceId))
|
||||
|
||||
let ciphertextMessage = try signalEncrypt(
|
||||
message: paddedPlaintext,
|
||||
@ -320,28 +320,3 @@ public class SMKSecretSessionCipher: NSObject {
|
||||
return Data(plaintextData)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Internal for testing
|
||||
|
||||
extension SMKSecretSessionCipher {
|
||||
|
||||
// Only allow nil contexts for testing
|
||||
func encryptMessage(
|
||||
recipient: SignalServiceAddress,
|
||||
deviceId: Int32,
|
||||
paddedPlaintext: Data,
|
||||
contentHint: UnidentifiedSenderMessageContent.ContentHint = .default,
|
||||
groupId: Data? = nil,
|
||||
senderCertificate: SenderCertificate,
|
||||
protocolContext: StoreContext? = nil
|
||||
) throws -> Data {
|
||||
try encryptMessage(
|
||||
recipient: recipient,
|
||||
deviceId: deviceId,
|
||||
paddedPlaintext: paddedPlaintext,
|
||||
contentHint: contentHint,
|
||||
groupId: groupId,
|
||||
senderCertificate: senderCertificate,
|
||||
protocolContext: protocolContext ?? NullContext())
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,11 +89,12 @@ public class SSKSessionStore: NSObject {
|
||||
keyValueStore.setObject(dictionary, key: accountId, transaction: transaction)
|
||||
}
|
||||
|
||||
@objc(containsActiveSessionForAddress:deviceId:transaction:)
|
||||
public func containsActiveSession(for address: SignalServiceAddress,
|
||||
deviceId: Int32,
|
||||
transaction: SDSAnyReadTransaction) -> Bool {
|
||||
owsAssertDebug(address.isValid)
|
||||
public func containsActiveSession(
|
||||
for serviceId: ServiceId,
|
||||
deviceId: Int32,
|
||||
transaction: SDSAnyReadTransaction
|
||||
) -> Bool {
|
||||
let address = SignalServiceAddress(serviceId)
|
||||
guard let accountId = OWSAccountIdFinder.accountId(forAddress: address, transaction: transaction) else {
|
||||
Logger.info("No accountId for: \(address). There must not be a stored session.")
|
||||
return false
|
||||
|
||||
@ -43,30 +43,35 @@ public class SenderKeyStore: NSObject {
|
||||
@objc
|
||||
public func recipientsInNeedOfSenderKey(
|
||||
for thread: TSThread,
|
||||
addresses: [SignalServiceAddress],
|
||||
serviceIds: [ServiceIdObjC],
|
||||
readTx: SDSAnyReadTransaction
|
||||
) -> [SignalServiceAddress] {
|
||||
var addressesNeedingSenderKey = Set(addresses)
|
||||
) -> [ServiceIdObjC] {
|
||||
var serviceIdsNeedingSenderKey = Set(serviceIds)
|
||||
|
||||
storageLock.withLock {
|
||||
// If we haven't saved a distributionId yet, then there's no way we have any keyMetadata cached
|
||||
// All intended recipients will certainly need an SKDM (if they even support sender key)
|
||||
guard let keyId = keyIdForSendingToThreadId(thread.threadUniqueId, readTx: readTx),
|
||||
let keyMetadata = getKeyMetadata(for: keyId, readTx: readTx) else {
|
||||
guard
|
||||
let keyId = keyIdForSendingToThreadId(thread.threadUniqueId, readTx: readTx),
|
||||
let keyMetadata = getKeyMetadata(for: keyId, readTx: readTx)
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
// Iterate over each cached recipient. If no new devices or reregistrations have occurred since
|
||||
// we last recorded an SKDM send, we can skip sending to them.
|
||||
for (address, sendInfo) in keyMetadata.sentKeyInfo {
|
||||
guard let serviceId = address.serviceIdObjC else {
|
||||
continue
|
||||
}
|
||||
do {
|
||||
let priorSendRecipientState = sendInfo.keyRecipient
|
||||
|
||||
// Only remove the recipient in question from our send targets if the cached state contains
|
||||
// every device from the current state. Any new devices mean we need to re-send.
|
||||
let currentRecipientState = try KeyRecipient.currentState(for: address, transaction: readTx)
|
||||
let currentRecipientState = try KeyRecipient.currentState(for: serviceId.wrappedValue, transaction: readTx)
|
||||
if priorSendRecipientState.containsEveryDevice(from: currentRecipientState) {
|
||||
addressesNeedingSenderKey.remove(address)
|
||||
serviceIdsNeedingSenderKey.remove(serviceId)
|
||||
}
|
||||
} catch {
|
||||
// It's likely there's no session for the current recipient. Maybe it was cleared?
|
||||
@ -79,23 +84,25 @@ public class SenderKeyStore: NSObject {
|
||||
}
|
||||
}
|
||||
}
|
||||
return Array(addressesNeedingSenderKey)
|
||||
return Array(serviceIdsNeedingSenderKey)
|
||||
}
|
||||
|
||||
/// Records that the current sender key for the `thread` has been sent to `participant`
|
||||
@objc
|
||||
public func recordSenderKeySent(
|
||||
for thread: TSThread,
|
||||
to address: SignalServiceAddress,
|
||||
to serviceId: ServiceIdObjC,
|
||||
timestamp: UInt64,
|
||||
writeTx: SDSAnyWriteTransaction) throws {
|
||||
try storageLock.withLock {
|
||||
guard let keyId = keyIdForSendingToThreadId(thread.threadUniqueId, writeTx: writeTx),
|
||||
let existingMetadata = getKeyMetadata(for: keyId, readTx: writeTx) else {
|
||||
guard
|
||||
let keyId = keyIdForSendingToThreadId(thread.threadUniqueId, writeTx: writeTx),
|
||||
let existingMetadata = getKeyMetadata(for: keyId, readTx: writeTx)
|
||||
else {
|
||||
throw OWSAssertionError("Failed to look up key metadata")
|
||||
}
|
||||
var updatedMetadata = existingMetadata
|
||||
try updatedMetadata.recordSKDMSent(at: timestamp, address: address, transaction: writeTx)
|
||||
try updatedMetadata.recordSKDMSent(at: timestamp, serviceId: serviceId.wrappedValue, transaction: writeTx)
|
||||
setMetadata(updatedMetadata, writeTx: writeTx)
|
||||
}
|
||||
}
|
||||
@ -422,35 +429,46 @@ private struct KeyRecipient: Codable, Dependencies {
|
||||
}
|
||||
|
||||
static func == (lhs: Device, rhs: Device) -> Bool {
|
||||
// We can only be sure that a device hasn't changed if the registrationIds are the same
|
||||
// If either registrationId is nil, that means the Device was constructed before we had a session
|
||||
// established for the device.
|
||||
// We can only be sure that a device hasn't changed if the registrationIds
|
||||
// are the same. If either registrationId is nil, that means the Device was
|
||||
// constructed before we had a session established for the device.
|
||||
//
|
||||
// If we end up trying to send a SenderKey message to a device without a session, this ensures that
|
||||
// this ensures that we will always send an SKDM to that device. A session will be created in order to
|
||||
// send the SKDM, so by the time we're ready to mark success we should have something to store.
|
||||
// If we end up trying to send a SenderKey message to a device without a
|
||||
// session, this ensures that we will always send an SKDM to that device. A
|
||||
// session will be created in order to send the SKDM, so by the time we're
|
||||
// ready to mark success we should have something to store.
|
||||
guard lhs.registrationId != nil, rhs.registrationId != nil else { return false }
|
||||
return lhs.registrationId == rhs.registrationId && lhs.deviceId == rhs.deviceId
|
||||
}
|
||||
}
|
||||
|
||||
let ownerAddress: SignalServiceAddress
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case devices
|
||||
|
||||
// We previously stored "ownerAddress" on the recipient. This is redundant
|
||||
// because "sentKeyInfo" stores the same value, and that's the one we use.
|
||||
}
|
||||
|
||||
let devices: Set<Device>
|
||||
private init(ownerAddress: SignalServiceAddress, devices: Set<Device>) {
|
||||
self.ownerAddress = ownerAddress
|
||||
|
||||
private init(devices: Set<Device>) {
|
||||
self.devices = devices
|
||||
}
|
||||
|
||||
/// Build a KeyRecipient for the given address by fetching all of the devices and corresponding registrationIds
|
||||
static func currentState(for address: SignalServiceAddress, transaction: SDSAnyReadTransaction) throws -> KeyRecipient {
|
||||
static func currentState(for serviceId: ServiceId, transaction: SDSAnyReadTransaction) throws -> KeyRecipient {
|
||||
guard
|
||||
let recipient = SignalRecipient.get(address: address, mustHaveDevices: false, transaction: transaction),
|
||||
let recipient = SignalRecipient.get(
|
||||
address: SignalServiceAddress(serviceId),
|
||||
mustHaveDevices: false,
|
||||
transaction: transaction
|
||||
),
|
||||
let deviceIds = recipient.deviceIds
|
||||
else {
|
||||
throw OWSAssertionError("Invalid device array")
|
||||
}
|
||||
|
||||
let protocolAddresses = try deviceIds.map { try ProtocolAddress(from: address, deviceId: $0) }
|
||||
let protocolAddresses = try deviceIds.map { try ProtocolAddress(uuid: serviceId.uuidValue, deviceId: $0) }
|
||||
let sessionStore = signalProtocolStore(for: .aci).sessionStore
|
||||
let devices: [Device] = try protocolAddresses.map {
|
||||
// We have to fetch the registrationId since deviceIds can be reused.
|
||||
@ -464,16 +482,11 @@ private struct KeyRecipient: Codable, Dependencies {
|
||||
|
||||
return Device(deviceId: $0.deviceId, registrationId: registrationId)
|
||||
}
|
||||
return KeyRecipient(ownerAddress: address, devices: Set(devices))
|
||||
return KeyRecipient(devices: Set(devices))
|
||||
}
|
||||
|
||||
/// Returns `true` as long as the argument does not contain any devices that are unknown to the receiver
|
||||
func containsEveryDevice(from other: KeyRecipient) -> Bool {
|
||||
guard ownerAddress == other.ownerAddress else {
|
||||
owsFailDebug("Address mismatch")
|
||||
return false
|
||||
}
|
||||
|
||||
let newDevices = other.devices.subtracting(self.devices)
|
||||
return newDevices.isEmpty
|
||||
}
|
||||
@ -541,10 +554,10 @@ private struct KeyMetadata {
|
||||
sentKeyInfo[address] = nil
|
||||
}
|
||||
|
||||
mutating func recordSKDMSent(at timestamp: UInt64, address: SignalServiceAddress, transaction: SDSAnyReadTransaction) throws {
|
||||
let recipient = try KeyRecipient.currentState(for: address, transaction: transaction)
|
||||
mutating func recordSKDMSent(at timestamp: UInt64, serviceId: ServiceId, transaction: SDSAnyReadTransaction) throws {
|
||||
let recipient = try KeyRecipient.currentState(for: serviceId, transaction: transaction)
|
||||
let sendInfo = SKDMSendInfo(skdmTimestamp: timestamp, keyRecipient: recipient)
|
||||
sentKeyInfo[address] = sendInfo
|
||||
sentKeyInfo[SignalServiceAddress(serviceId)] = sendInfo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -54,12 +54,12 @@ class _ChangePhoneNumberPniManager_MessageSenderMock: _ChangePhoneNumberPniManag
|
||||
func buildDeviceMessage(
|
||||
forMessagePlaintextContent messagePlaintextContent: Data?,
|
||||
messageEncryptionStyle: EncryptionStyle,
|
||||
recipientServiceId: ServiceId,
|
||||
recipientAccountId: String,
|
||||
recipientDeviceId: NSNumber,
|
||||
recipientId: String,
|
||||
serviceId: ServiceId,
|
||||
deviceId: NSNumber,
|
||||
isOnlineMessage: Bool,
|
||||
isTransientSenderKeyDistributionMessage: Bool,
|
||||
isStorySendMessage: Bool,
|
||||
isStoryMessage: Bool,
|
||||
isResendRequestMessage: Bool,
|
||||
udSendingParamsProvider: UDSendingParamsProvider?
|
||||
) throws -> DeviceMessage? {
|
||||
@ -74,7 +74,7 @@ class _ChangePhoneNumberPniManager_MessageSenderMock: _ChangePhoneNumberPniManag
|
||||
case let .valid(registrationId):
|
||||
return DeviceMessage(
|
||||
type: .ciphertext,
|
||||
destinationDeviceId: recipientDeviceId.uint32Value,
|
||||
destinationDeviceId: deviceId.uint32Value,
|
||||
destinationRegistrationId: registrationId,
|
||||
serializedMessage: Cryptography.generateRandomBytes(32)
|
||||
)
|
||||
|
||||
@ -21,13 +21,11 @@ final class DisappearingMessageFinderTest: SSKBaseTestSwift {
|
||||
SignalServiceAddress(phoneNumber: "+12225550123")
|
||||
}
|
||||
|
||||
func otherAddress() -> SignalServiceAddress {
|
||||
SignalServiceAddress(phoneNumber: "+13335550198")
|
||||
}
|
||||
private lazy var otherAddress = SignalServiceAddress(uuid: UUID(), phoneNumber: "+13335550198")
|
||||
|
||||
func thread(with transaction: SDSAnyWriteTransaction) -> TSThread {
|
||||
TSContactThread.getOrCreateThread(
|
||||
withContactAddress: otherAddress(),
|
||||
withContactAddress: otherAddress,
|
||||
transaction: transaction
|
||||
)
|
||||
}
|
||||
@ -48,7 +46,7 @@ final class DisappearingMessageFinderTest: SSKBaseTestSwift {
|
||||
|
||||
let incomingMessageBuilder = TSIncomingMessageBuilder(thread: thread, messageBody: body)
|
||||
incomingMessageBuilder.timestamp = 1
|
||||
incomingMessageBuilder.authorAddress = otherAddress()
|
||||
incomingMessageBuilder.authorAddress = otherAddress
|
||||
incomingMessageBuilder.expiresInSeconds = expiresInSeconds
|
||||
let message = incomingMessageBuilder.build()
|
||||
message.anyInsert(transaction: transaction)
|
||||
@ -215,12 +213,12 @@ final class DisappearingMessageFinderTest: SSKBaseTestSwift {
|
||||
write { transaction in
|
||||
// Mark outgoing message as "sent", "delivered" or "delivered and read" using production methods.
|
||||
expiringSentOutgoingMessage.update(
|
||||
withSentRecipient: otherAddress(),
|
||||
withSentRecipient: otherAddress.serviceIdObjC!,
|
||||
wasSentByUD: false,
|
||||
transaction: transaction
|
||||
)
|
||||
expiringDeliveredOutgoingMessage.update(
|
||||
withDeliveredRecipient: otherAddress(),
|
||||
withDeliveredRecipient: otherAddress,
|
||||
recipientDeviceId: 0,
|
||||
deliveryTimestamp: Date.ows_millisecondTimestamp(),
|
||||
context: PassthroughDeliveryReceiptContext(),
|
||||
@ -228,7 +226,7 @@ final class DisappearingMessageFinderTest: SSKBaseTestSwift {
|
||||
)
|
||||
let nowMs = Date.ows_millisecondTimestamp()
|
||||
expiringDeliveredAndReadOutgoingMessage.update(
|
||||
withReadRecipient: otherAddress(),
|
||||
withReadRecipient: otherAddress,
|
||||
recipientDeviceId: 0,
|
||||
readTimestamp: nowMs,
|
||||
transaction: transaction
|
||||
|
||||
@ -28,7 +28,8 @@ class TSOutgoingMessageTest: SSKBaseTestSwift {
|
||||
|
||||
func testShouldNotStartExpireTimerWithMessageThatDoesNotExpire() {
|
||||
write { transaction in
|
||||
let otherAddress = SignalServiceAddress(phoneNumber: "+12223334444")
|
||||
let otherServiceId = ServiceId(UUID())
|
||||
let otherAddress = SignalServiceAddress(uuid: otherServiceId.uuidValue, phoneNumber: "+12223334444")
|
||||
let thread = TSContactThread.getOrCreateThread(withContactAddress: otherAddress, transaction: transaction)
|
||||
let messageBuilder = TSOutgoingMessageBuilder.outgoingMessageBuilder(thread: thread, messageBody: nil)
|
||||
messageBuilder.timestamp = 100
|
||||
@ -36,7 +37,7 @@ class TSOutgoingMessageTest: SSKBaseTestSwift {
|
||||
|
||||
XCTAssertFalse(message.shouldStartExpireTimer())
|
||||
|
||||
message.update(withSentRecipient: otherAddress, wasSentByUD: false, transaction: transaction)
|
||||
message.update(withSentRecipient: ServiceIdObjC(otherServiceId), wasSentByUD: false, transaction: transaction)
|
||||
|
||||
XCTAssertFalse(message.shouldStartExpireTimer())
|
||||
}
|
||||
@ -44,7 +45,8 @@ class TSOutgoingMessageTest: SSKBaseTestSwift {
|
||||
|
||||
func testShouldStartExpireTimerWithSentMessage() {
|
||||
write { transaction in
|
||||
let otherAddress = SignalServiceAddress(phoneNumber: "+12223334444")
|
||||
let otherServiceId = ServiceId(UUID())
|
||||
let otherAddress = SignalServiceAddress(uuid: otherServiceId.uuidValue, phoneNumber: "+12223334444")
|
||||
let thread = TSContactThread.getOrCreateThread(withContactAddress: otherAddress, transaction: transaction)
|
||||
let messageBuilder = TSOutgoingMessageBuilder.outgoingMessageBuilder(thread: thread, messageBody: nil)
|
||||
messageBuilder.timestamp = 100
|
||||
@ -53,7 +55,7 @@ class TSOutgoingMessageTest: SSKBaseTestSwift {
|
||||
|
||||
XCTAssertFalse(message.shouldStartExpireTimer())
|
||||
|
||||
message.update(withSentRecipient: otherAddress, wasSentByUD: false, transaction: transaction)
|
||||
message.update(withSentRecipient: ServiceIdObjC(otherServiceId), wasSentByUD: false, transaction: transaction)
|
||||
|
||||
XCTAssertTrue(message.shouldStartExpireTimer())
|
||||
}
|
||||
@ -112,7 +114,8 @@ class TSOutgoingMessageTest: SSKBaseTestSwift {
|
||||
|
||||
func testReceiptClearsSharePhoneNumber() {
|
||||
write { transaction in
|
||||
let otherAddress = SignalServiceAddress(uuid: UUID(), phoneNumber: "+12223334444")
|
||||
let otherServiceId = ServiceId(UUID())
|
||||
let otherAddress = SignalServiceAddress(uuid: otherServiceId.uuidValue, phoneNumber: "+12223334444")
|
||||
identityManager.setShouldSharePhoneNumber(with: otherAddress, transaction: transaction)
|
||||
|
||||
let thread = TSContactThread.getOrCreateThread(withContactAddress: otherAddress, transaction: transaction)
|
||||
@ -121,7 +124,7 @@ class TSOutgoingMessageTest: SSKBaseTestSwift {
|
||||
let message = messageBuilder.build(transaction: transaction)
|
||||
let messageData = message.buildPlainTextData(thread, transaction: transaction)!
|
||||
|
||||
message.update(withSentRecipient: otherAddress, wasSentByUD: true, transaction: transaction)
|
||||
message.update(withSentRecipient: ServiceIdObjC(otherServiceId), wasSentByUD: true, transaction: transaction)
|
||||
|
||||
let payloadId = MessageSendLog.recordPayload(messageData, forMessageBeingSent: message, transaction: transaction) as! Int64
|
||||
MessageSendLog.recordPendingDelivery(payloadId: payloadId,
|
||||
@ -145,7 +148,8 @@ class TSOutgoingMessageTest: SSKBaseTestSwift {
|
||||
|
||||
func testReceiptClearsSharePhoneNumberOnlyOnLastDevice() {
|
||||
write { transaction in
|
||||
let otherAddress = SignalServiceAddress(uuid: UUID(), phoneNumber: "+12223334444")
|
||||
let otherServiceId = ServiceId(UUID())
|
||||
let otherAddress = SignalServiceAddress(uuid: otherServiceId.uuidValue, phoneNumber: "+12223334444")
|
||||
identityManager.setShouldSharePhoneNumber(with: otherAddress, transaction: transaction)
|
||||
|
||||
let thread = TSContactThread.getOrCreateThread(withContactAddress: otherAddress, transaction: transaction)
|
||||
@ -154,7 +158,7 @@ class TSOutgoingMessageTest: SSKBaseTestSwift {
|
||||
let message = messageBuilder.build(transaction: transaction)
|
||||
let messageData = message.buildPlainTextData(thread, transaction: transaction)!
|
||||
|
||||
message.update(withSentRecipient: otherAddress, wasSentByUD: true, transaction: transaction)
|
||||
message.update(withSentRecipient: ServiceIdObjC(otherServiceId), wasSentByUD: true, transaction: transaction)
|
||||
|
||||
let payloadId = MessageSendLog.recordPayload(messageData, forMessageBeingSent: message, transaction: transaction) as! Int64
|
||||
MessageSendLog.recordPendingDelivery(payloadId: payloadId,
|
||||
@ -193,7 +197,8 @@ class TSOutgoingMessageTest: SSKBaseTestSwift {
|
||||
|
||||
func testReceiptDoesNotClearSharePhoneNumberIfNotSealedSender() {
|
||||
write { transaction in
|
||||
let otherAddress = SignalServiceAddress(uuid: UUID(), phoneNumber: "+12223334444")
|
||||
let otherServiceId = ServiceId(UUID())
|
||||
let otherAddress = SignalServiceAddress(uuid: otherServiceId.uuidValue, phoneNumber: "+12223334444")
|
||||
identityManager.setShouldSharePhoneNumber(with: otherAddress, transaction: transaction)
|
||||
|
||||
let thread = TSContactThread.getOrCreateThread(withContactAddress: otherAddress, transaction: transaction)
|
||||
@ -202,7 +207,7 @@ class TSOutgoingMessageTest: SSKBaseTestSwift {
|
||||
let message = messageBuilder.build(transaction: transaction)
|
||||
let messageData = message.buildPlainTextData(thread, transaction: transaction)!
|
||||
|
||||
message.update(withSentRecipient: otherAddress, wasSentByUD: false, transaction: transaction)
|
||||
message.update(withSentRecipient: ServiceIdObjC(otherServiceId), wasSentByUD: false, transaction: transaction)
|
||||
|
||||
let payloadId = MessageSendLog.recordPayload(messageData, forMessageBeingSent: message, transaction: transaction) as! Int64
|
||||
MessageSendLog.recordPendingDelivery(payloadId: payloadId,
|
||||
@ -227,7 +232,8 @@ class TSOutgoingMessageTest: SSKBaseTestSwift {
|
||||
|
||||
func testReceiptDoesNotClearSharePhoneNumberIfNoPniSignature() {
|
||||
write { transaction in
|
||||
let otherAddress = SignalServiceAddress(uuid: UUID(), phoneNumber: "+12223334444")
|
||||
let otherServiceId = ServiceId(UUID())
|
||||
let otherAddress = SignalServiceAddress(uuid: otherServiceId.uuidValue, phoneNumber: "+12223334444")
|
||||
|
||||
let thread = TSContactThread.getOrCreateThread(withContactAddress: otherAddress, transaction: transaction)
|
||||
let messageBuilder = TSOutgoingMessageBuilder.outgoingMessageBuilder(thread: thread, messageBody: nil)
|
||||
@ -235,7 +241,7 @@ class TSOutgoingMessageTest: SSKBaseTestSwift {
|
||||
let message = messageBuilder.build(transaction: transaction)
|
||||
let messageData = message.buildPlainTextData(thread, transaction: transaction)!
|
||||
|
||||
message.update(withSentRecipient: otherAddress, wasSentByUD: true, transaction: transaction)
|
||||
message.update(withSentRecipient: ServiceIdObjC(otherServiceId), wasSentByUD: true, transaction: transaction)
|
||||
|
||||
let payloadId = MessageSendLog.recordPayload(messageData, forMessageBeingSent: message, transaction: transaction) as! Int64
|
||||
MessageSendLog.recordPendingDelivery(payloadId: payloadId,
|
||||
@ -260,7 +266,8 @@ class TSOutgoingMessageTest: SSKBaseTestSwift {
|
||||
}
|
||||
|
||||
func testReceiptDoesNotClearSharePhoneNumberIfPniHasChanged() {
|
||||
let otherAddress = SignalServiceAddress(uuid: UUID(), phoneNumber: "+12223334444")
|
||||
let otherServiceId = ServiceId(UUID())
|
||||
let otherAddress = SignalServiceAddress(uuid: otherServiceId.uuidValue, phoneNumber: "+12223334444")
|
||||
var message: TSOutgoingMessage!
|
||||
|
||||
write { transaction in
|
||||
@ -272,7 +279,7 @@ class TSOutgoingMessageTest: SSKBaseTestSwift {
|
||||
message = messageBuilder.build(transaction: transaction)
|
||||
let messageData = message.buildPlainTextData(thread, transaction: transaction)!
|
||||
|
||||
message.update(withSentRecipient: otherAddress, wasSentByUD: true, transaction: transaction)
|
||||
message.update(withSentRecipient: ServiceIdObjC(otherServiceId), wasSentByUD: true, transaction: transaction)
|
||||
|
||||
let payloadId = MessageSendLog.recordPayload(messageData, forMessageBeingSent: message, transaction: transaction) as! Int64
|
||||
MessageSendLog.recordPendingDelivery(payloadId: payloadId,
|
||||
|
||||
@ -226,7 +226,7 @@ class OWSUDManagerTest: SSKBaseTestSwift {
|
||||
// Ensure UD is enabled by setting our own access level to enabled.
|
||||
udManagerImpl.setUnidentifiedAccessMode(.enabled, address: localAddress)
|
||||
|
||||
let bobRecipientAddress = SignalServiceAddress(phoneNumber: "+13213214322")
|
||||
let bobRecipientAddress = SignalServiceAddress(uuid: UUID(), phoneNumber: "+13213214322")
|
||||
XCTAssertFalse(bobRecipientAddress.isLocalAddress)
|
||||
write { transaction in
|
||||
self.profileManager.setProfileKeyData(OWSAES256Key.generateRandom().keyData,
|
||||
@ -239,9 +239,11 @@ class OWSUDManagerTest: SSKBaseTestSwift {
|
||||
firstly {
|
||||
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
|
||||
}.done { senderCertificates in
|
||||
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
|
||||
requireSyncAccess: false,
|
||||
senderCertificates: senderCertificates)!
|
||||
let sendingAccess = self.udManagerImpl.udSendingAccess(
|
||||
for: bobRecipientAddress.serviceIdObjC!,
|
||||
requireSyncAccess: false,
|
||||
senderCertificates: senderCertificates
|
||||
)!
|
||||
XCTAssertEqual(.unknown, sendingAccess.udAccess.udAccessMode)
|
||||
XCTAssertFalse(sendingAccess.udAccess.isRandomKey)
|
||||
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),
|
||||
@ -258,9 +260,11 @@ class OWSUDManagerTest: SSKBaseTestSwift {
|
||||
firstly {
|
||||
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
|
||||
}.done { senderCertificates in
|
||||
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
|
||||
requireSyncAccess: false,
|
||||
senderCertificates: senderCertificates)!
|
||||
let sendingAccess = self.udManagerImpl.udSendingAccess(
|
||||
for: bobRecipientAddress.serviceIdObjC!,
|
||||
requireSyncAccess: false,
|
||||
senderCertificates: senderCertificates
|
||||
)!
|
||||
XCTAssertEqual(.unknown, sendingAccess.udAccess.udAccessMode)
|
||||
XCTAssertFalse(sendingAccess.udAccess.isRandomKey)
|
||||
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),
|
||||
@ -277,9 +281,11 @@ class OWSUDManagerTest: SSKBaseTestSwift {
|
||||
firstly {
|
||||
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
|
||||
}.done { senderCertificates in
|
||||
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
|
||||
requireSyncAccess: false,
|
||||
senderCertificates: senderCertificates)!
|
||||
let sendingAccess = self.udManagerImpl.udSendingAccess(
|
||||
for: bobRecipientAddress.serviceIdObjC!,
|
||||
requireSyncAccess: false,
|
||||
senderCertificates: senderCertificates
|
||||
)!
|
||||
XCTAssertEqual(.unknown, sendingAccess.udAccess.udAccessMode)
|
||||
XCTAssertFalse(sendingAccess.udAccess.isRandomKey)
|
||||
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),
|
||||
@ -292,9 +298,11 @@ class OWSUDManagerTest: SSKBaseTestSwift {
|
||||
firstly {
|
||||
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
|
||||
}.done { senderCertificates in
|
||||
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
|
||||
requireSyncAccess: false,
|
||||
senderCertificates: senderCertificates)!
|
||||
let sendingAccess = self.udManagerImpl.udSendingAccess(
|
||||
for: bobRecipientAddress.serviceIdObjC!,
|
||||
requireSyncAccess: false,
|
||||
senderCertificates: senderCertificates
|
||||
)!
|
||||
XCTAssertEqual(.unknown, sendingAccess.udAccess.udAccessMode)
|
||||
XCTAssertFalse(sendingAccess.udAccess.isRandomKey)
|
||||
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),
|
||||
@ -329,9 +337,11 @@ class OWSUDManagerTest: SSKBaseTestSwift {
|
||||
firstly {
|
||||
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
|
||||
}.done { senderCertificates in
|
||||
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
|
||||
requireSyncAccess: false,
|
||||
senderCertificates: senderCertificates)!
|
||||
let sendingAccess = self.udManagerImpl.udSendingAccess(
|
||||
for: bobRecipientAddress.serviceIdObjC!,
|
||||
requireSyncAccess: false,
|
||||
senderCertificates: senderCertificates
|
||||
)!
|
||||
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),
|
||||
self.defaultSenderCert.serialize())
|
||||
}.expect(timeout: 1.0)
|
||||
@ -346,9 +356,11 @@ class OWSUDManagerTest: SSKBaseTestSwift {
|
||||
firstly {
|
||||
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
|
||||
}.done { senderCertificates in
|
||||
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
|
||||
requireSyncAccess: false,
|
||||
senderCertificates: senderCertificates)!
|
||||
let sendingAccess = self.udManagerImpl.udSendingAccess(
|
||||
for: bobRecipientAddress.serviceIdObjC!,
|
||||
requireSyncAccess: false,
|
||||
senderCertificates: senderCertificates
|
||||
)!
|
||||
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),
|
||||
self.defaultSenderCert.serialize())
|
||||
}.expect(timeout: 1.0)
|
||||
@ -363,9 +375,11 @@ class OWSUDManagerTest: SSKBaseTestSwift {
|
||||
firstly {
|
||||
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
|
||||
}.done { senderCertificates in
|
||||
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
|
||||
requireSyncAccess: false,
|
||||
senderCertificates: senderCertificates)!
|
||||
let sendingAccess = self.udManagerImpl.udSendingAccess(
|
||||
for: bobRecipientAddress.serviceIdObjC!,
|
||||
requireSyncAccess: false,
|
||||
senderCertificates: senderCertificates
|
||||
)!
|
||||
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),
|
||||
self.defaultSenderCert.serialize())
|
||||
}.expect(timeout: 1.0)
|
||||
@ -383,9 +397,11 @@ class OWSUDManagerTest: SSKBaseTestSwift {
|
||||
firstly {
|
||||
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
|
||||
}.done { senderCertificates in
|
||||
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
|
||||
requireSyncAccess: false,
|
||||
senderCertificates: senderCertificates)!
|
||||
let sendingAccess = self.udManagerImpl.udSendingAccess(
|
||||
for: bobRecipientAddress.serviceIdObjC!,
|
||||
requireSyncAccess: false,
|
||||
senderCertificates: senderCertificates
|
||||
)!
|
||||
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),
|
||||
self.uuidOnlySenderCert.serialize())
|
||||
}.expect(timeout: 1.0)
|
||||
|
||||
@ -43,10 +43,15 @@ class SMKSecretSessionCipherTest: SSKBaseTestSwift {
|
||||
// senderCertificate, "smert za smert".getBytes());
|
||||
// NOTE: The java tests don't bother padding the plaintext.
|
||||
let alicePlaintext = "smert za smert".data(using: String.Encoding.utf8)!
|
||||
let ciphertext = try! aliceCipher.encryptMessage(recipient: bobMockClient.address,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: alicePlaintext,
|
||||
senderCertificate: senderCertificate)
|
||||
let ciphertext = try! aliceCipher.encryptMessage(
|
||||
for: bobMockClient.address.serviceId!,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: alicePlaintext,
|
||||
contentHint: .default,
|
||||
groupId: nil,
|
||||
senderCertificate: senderCertificate,
|
||||
protocolContext: NullContext()
|
||||
)
|
||||
|
||||
// SealedSessionCipher bobCipher = new SealedSessionCipher(bobStore, new SignalProtocolAddress("+14152222222", 1));
|
||||
let bobCipher: SMKSecretSessionCipher = try! bobMockClient.createSecretSessionCipher()
|
||||
@ -100,12 +105,15 @@ class SMKSecretSessionCipherTest: SSKBaseTestSwift {
|
||||
let alicePlaintext = "и вот я".data(using: String.Encoding.utf8)!
|
||||
let aliceGroupId = Randomness.generateRandomBytes(6)
|
||||
let aliceContentHint = UnidentifiedSenderMessageContent.ContentHint.implicit
|
||||
let ciphertext = try! aliceCipher.encryptMessage(recipient: bobMockClient.address,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: alicePlaintext,
|
||||
contentHint: aliceContentHint,
|
||||
groupId: aliceGroupId,
|
||||
senderCertificate: senderCertificate)
|
||||
let ciphertext = try! aliceCipher.encryptMessage(
|
||||
for: bobMockClient.address.serviceId!,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: alicePlaintext,
|
||||
contentHint: aliceContentHint,
|
||||
groupId: aliceGroupId,
|
||||
senderCertificate: senderCertificate,
|
||||
protocolContext: NullContext()
|
||||
)
|
||||
|
||||
// SecretSessionCipher bobCipher = new SecretSessionCipher(bobStore);
|
||||
let bobCipher: SMKSecretSessionCipher = try! bobMockClient.createSecretSessionCipher()
|
||||
@ -179,12 +187,15 @@ class SMKSecretSessionCipherTest: SSKBaseTestSwift {
|
||||
let aliceGroupId = Randomness.generateRandomBytes(6)
|
||||
let aliceContentHint = UnidentifiedSenderMessageContent.ContentHint.resendable
|
||||
|
||||
let ciphertext = try! aliceCipher.encryptMessage(recipient: bobMockClient.address,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: alicePlaintext,
|
||||
contentHint: aliceContentHint,
|
||||
groupId: aliceGroupId,
|
||||
senderCertificate: senderCertificate)
|
||||
let ciphertext = try! aliceCipher.encryptMessage(
|
||||
for: bobMockClient.address.serviceId!,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: alicePlaintext,
|
||||
contentHint: aliceContentHint,
|
||||
groupId: aliceGroupId,
|
||||
senderCertificate: senderCertificate,
|
||||
protocolContext: NullContext()
|
||||
)
|
||||
|
||||
// SecretSessionCipher bobCipher = new SecretSessionCipher(bobStore);
|
||||
let bobCipher: SMKSecretSessionCipher = try! bobMockClient.createSecretSessionCipher()
|
||||
@ -257,10 +268,15 @@ class SMKSecretSessionCipherTest: SSKBaseTestSwift {
|
||||
// senderCertificate, "smert za smert".getBytes());
|
||||
// NOTE: The java tests don't bother padding the plaintext.
|
||||
let alicePlaintext = "smert za smert".data(using: String.Encoding.utf8)!
|
||||
let ciphertext = try! aliceCipher.encryptMessage(recipient: bobMockClient.address,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: alicePlaintext,
|
||||
senderCertificate: senderCertificate)
|
||||
let ciphertext = try! aliceCipher.encryptMessage(
|
||||
for: bobMockClient.address.serviceId!,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: alicePlaintext,
|
||||
contentHint: .default,
|
||||
groupId: nil,
|
||||
senderCertificate: senderCertificate,
|
||||
protocolContext: NullContext()
|
||||
)
|
||||
|
||||
// SecretSessionCipher bobCipher = new SecretSessionCipher(bobStore);
|
||||
let bobCipher: SMKSecretSessionCipher = try! bobMockClient.createSecretSessionCipher()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user