From 074046b98e0f6e95b45fa8dcdbbe40a8031f53f3 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 30 Jan 2018 15:05:04 -0500 Subject: [PATCH] Add protocol context to protocol kit. --- .../DebugUI/DebugUISessionState.m | 2 +- .../ShowGroupMembersViewController.m | 4 +- .../SharingThreadPickerViewController.m | 2 +- .../src/Account/TSAccountManager.m | 8 +- .../src/Account/TSPreKeyManager.m | 4 +- .../src/Contacts/Threads/TSContactThread.m | 2 +- .../src/Devices/OWSRecordTranscriptJob.m | 4 +- ...SInvalidIdentityKeyReceivingErrorMessage.m | 8 +- .../TSInvalidIdentityKeySendingErrorMessage.m | 12 +- .../src/Messages/OWSBatchMessageProcessor.h | 6 +- .../src/Messages/OWSBatchMessageProcessor.m | 30 +- .../src/Messages/OWSIdentityManager.h | 4 + .../src/Messages/OWSIdentityManager.m | 9 + .../src/Messages/OWSMessageDecrypter.h | 5 +- .../src/Messages/OWSMessageDecrypter.m | 77 +- .../src/Messages/OWSMessageManager.h | 6 +- .../src/Messages/OWSMessageManager.m | 44 +- .../src/Messages/OWSMessageReceiver.m | 14 +- .../src/Messages/OWSMessageSender.m | 110 +- .../src/Messages/OWSMessageUtils.h | 13 +- .../src/Messages/OWSMessageUtils.m | 1063 +---------------- .../src/Security/OWSFingerprintBuilder.m | 7 +- .../TSStorageManager+SessionStore.m | 8 +- .../TSStorageManager+SignedPreKeyStore.m | 2 +- 24 files changed, 197 insertions(+), 1247 deletions(-) diff --git a/Signal/src/ViewControllers/DebugUI/DebugUISessionState.m b/Signal/src/ViewControllers/DebugUI/DebugUISessionState.m index 747d2af85..f2ce05caa 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUISessionState.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUISessionState.m @@ -41,7 +41,7 @@ NS_ASSUME_NONNULL_BEGIN OWSIdentityManager *identityManager = [OWSIdentityManager sharedManager]; NSString *recipientId = [thread contactIdentifier]; - NSData *currentKey = [identityManager identityKeyForRecipientId:recipientId]; + NSData *currentKey = [identityManager identityKeyForRecipientIdWOT:recipientId]; NSMutableData *flippedKey = [NSMutableData new]; const char *currentKeyBytes = currentKey.bytes; for (NSUInteger i = 0; i < currentKey.length; i++) { diff --git a/Signal/src/ViewControllers/ShowGroupMembersViewController.m b/Signal/src/ViewControllers/ShowGroupMembersViewController.m index 0b65a8d17..d73258406 100644 --- a/Signal/src/ViewControllers/ShowGroupMembersViewController.m +++ b/Signal/src/ViewControllers/ShowGroupMembersViewController.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "ShowGroupMembersViewController.h" @@ -246,7 +246,7 @@ NS_ASSUME_NONNULL_BEGIN for (NSString *recipientId in recipientIds) { OWSVerificationState verificationState = [identityManger verificationStateForRecipientId:recipientId]; if (verificationState == OWSVerificationStateNoLongerVerified) { - NSData *identityKey = [identityManger identityKeyForRecipientId:recipientId]; + NSData *identityKey = [identityManger identityKeyForRecipientIdWOT:recipientId]; if (identityKey.length < 1) { OWSFail(@"Missing identity key for: %@", recipientId); continue; diff --git a/SignalMessaging/attachments/SharingThreadPickerViewController.m b/SignalMessaging/attachments/SharingThreadPickerViewController.m index 2ea07b5fc..7338ddec1 100644 --- a/SignalMessaging/attachments/SharingThreadPickerViewController.m +++ b/SignalMessaging/attachments/SharingThreadPickerViewController.m @@ -404,7 +404,7 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion); } case OWSVerificationStateNoLongerVerified: { DDLogInfo(@"%@ marked recipient: %@ as default verification status.", self.logTag, recipientId); - NSData *identityKey = [[OWSIdentityManager sharedManager] identityKeyForRecipientId:recipientId]; + NSData *identityKey = [[OWSIdentityManager sharedManager] identityKeyForRecipientIdWOT:recipientId]; OWSAssert(identityKey); [[OWSIdentityManager sharedManager] setVerificationState:OWSVerificationStateDefault identityKey:identityKey diff --git a/SignalServiceKit/src/Account/TSAccountManager.m b/SignalServiceKit/src/Account/TSAccountManager.m index 463f2a1cc..f0cd99ac6 100644 --- a/SignalServiceKit/src/Account/TSAccountManager.m +++ b/SignalServiceKit/src/Account/TSAccountManager.m @@ -106,9 +106,7 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling [transaction removeAllObjectsInCollection:TSAccountManager_UserAccountCollection]; }]; } - dispatch_async([OWSDispatch sessionStoreQueue], ^{ - [[TSStorageManager sharedManager] resetSessionStore]; - }); + [[TSStorageManager sharedManager] resetSessionStore]; } + (BOOL)isRegistered @@ -201,9 +199,11 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling - (uint32_t)getOrGenerateRegistrationId { + __block uint32_t result; [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - [self getOrGenerateRegistrationId:transaction]; + result = [self getOrGenerateRegistrationId:transaction]; }]; + return result; } - (uint32_t)getOrGenerateRegistrationId:(YapDatabaseReadWriteTransaction *)transaction diff --git a/SignalServiceKit/src/Account/TSPreKeyManager.m b/SignalServiceKit/src/Account/TSPreKeyManager.m index 9078842f3..b9afdbbe7 100644 --- a/SignalServiceKit/src/Account/TSPreKeyManager.m +++ b/SignalServiceKit/src/Account/TSPreKeyManager.m @@ -134,11 +134,11 @@ static const NSTimeInterval kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * RefreshPreKeysMode modeCopy = mode; TSStorageManager *storageManager = [TSStorageManager sharedManager]; - ECKeyPair *identityKeyPair = [[OWSIdentityManager sharedManager] identityKeyPair]; + ECKeyPair *identityKeyPair = [[OWSIdentityManager sharedManager] identityKeyPairWithoutProtocolContext]; if (!identityKeyPair) { [[OWSIdentityManager sharedManager] generateNewIdentityKey]; - identityKeyPair = [[OWSIdentityManager sharedManager] identityKeyPair]; + identityKeyPair = [[OWSIdentityManager sharedManager] identityKeyPairWithoutProtocolContext]; // Switch modes if necessary. modeCopy = RefreshPreKeysMode_SignedAndOneTime; diff --git a/SignalServiceKit/src/Contacts/Threads/TSContactThread.m b/SignalServiceKit/src/Contacts/Threads/TSContactThread.m index 599ea9202..f59d407b5 100644 --- a/SignalServiceKit/src/Contacts/Threads/TSContactThread.m +++ b/SignalServiceKit/src/Contacts/Threads/TSContactThread.m @@ -111,7 +111,7 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)hasSafetyNumbers { - return !![[OWSIdentityManager sharedManager] identityKeyForRecipientId:self.contactIdentifier]; + return !![[OWSIdentityManager sharedManager] identityKeyForRecipientIdWOT:self.contactIdentifier]; } - (NSString *)name diff --git a/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m b/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m index 18d96d43c..c50b404f8 100644 --- a/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m +++ b/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m @@ -68,9 +68,7 @@ NS_ASSUME_NONNULL_BEGIN TSThread *thread = [transcript threadWithTransaction:transaction]; if (transcript.isEndSessionMessage) { DDLogInfo(@"%@ EndSession was sent to recipient: %@.", self.logTag, transcript.recipientId); - dispatch_async([OWSDispatch sessionStoreQueue], ^{ - [self.storageManager deleteAllSessionsForContact:transcript.recipientId protocolContext:protocolContext]; - }); + [self.storageManager deleteAllSessionsForContact:transcript.recipientId protocolContext:transaction]; [[[TSInfoMessage alloc] initWithTimestamp:transcript.timestamp inThread:thread messageType:TSInfoMessageTypeSessionDidEnd] saveWithTransaction:transaction]; diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m index 8f29e5cd5..1f2a23eff 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m @@ -10,6 +10,7 @@ #import "TSContactThread.h" #import "TSDatabaseView.h" #import "TSErrorMessage_privateConstructor.h" +#import "TSStorageManager+SessionStore.h" #import "TSStorageManager.h" #import #import @@ -80,10 +81,11 @@ NS_ASSUME_NONNULL_BEGIN } // Saving a new identity mutates the session store so it must happen on the sessionStoreQueue - dispatch_async([OWSDispatch sessionStoreQueue], ^{ + [TSStorageManager.protocolStoreDBConnection asyncReadWriteWithBlock:^( + YapDatabaseReadWriteTransaction *transaction) { [[OWSIdentityManager sharedManager] saveRemoteIdentity:newKey recipientId:self.envelope.source - protocolContext:protocolContext]; + protocolContext:transaction]; dispatch_async(dispatch_get_main_queue(), ^{ // Decrypt this and any old messages for the newly accepted key @@ -99,7 +101,7 @@ NS_ASSUME_NONNULL_BEGIN [errorMessage remove]; } }); - }); + }]; } - (nullable NSData *)newIdentityKey diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m index a44326a3b..875b0594c 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m @@ -10,6 +10,7 @@ #import "TSContactThread.h" #import "TSErrorMessage_privateConstructor.h" #import "TSOutgoingMessage.h" +#import "TSStorageManager+SessionStore.h" #import NS_ASSUME_NONNULL_BEGIN @@ -57,11 +58,12 @@ NSString *TSInvalidRecipientKey = @"TSInvalidRecipientKey"; return; } - dispatch_async([OWSDispatch sessionStoreQueue], ^{ - [[OWSIdentityManager sharedManager] saveRemoteIdentity:newIdentityKey - recipientId:self.recipientId - protocolContext:protocolContext]; - }); + [TSStorageManager.protocolStoreDBConnection + asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + [[OWSIdentityManager sharedManager] saveRemoteIdentity:newIdentityKey + recipientId:self.recipientId + protocolContext:transaction]; + }]; } - (nullable NSData *)newIdentityKey diff --git a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.h b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.h index 74e4dcdf9..c929dcee9 100644 --- a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.h +++ b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.h @@ -6,6 +6,7 @@ NS_ASSUME_NONNULL_BEGIN @class OWSSignalServiceProtosEnvelope; @class OWSStorage; +@class YapDatabaseReadWriteTransaction; // This class is used to write incoming (decrypted, unprocessed) // messages to a durable queue and then process them in batches, @@ -15,7 +16,10 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)sharedInstance; + (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage; -- (void)enqueueEnvelopeData:(NSData *)envelopeData plaintextData:(NSData *_Nullable)plaintextData; +- (void)enqueueEnvelopeData:(NSData *)envelopeData + plaintextData:(NSData *_Nullable)plaintextData + transaction:(YapDatabaseReadWriteTransaction *)transaction; + - (void)handleAnyUnprocessedEnvelopesAsync; @end diff --git a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m index 3a9afe3d5..e79a58d6f 100644 --- a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m +++ b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m @@ -136,14 +136,16 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo return [jobs copy]; } -- (void)addJobWithEnvelopeData:(NSData *)envelopeData plaintextData:(NSData *_Nullable)plaintextData +- (void)addJobWithEnvelopeData:(NSData *)envelopeData + plaintextData:(NSData *_Nullable)plaintextData + transaction:(YapDatabaseReadWriteTransaction *)transaction { - // We need to persist the decrypted envelope data ASAP to prevent data loss. - [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - OWSMessageContentJob *job = - [[OWSMessageContentJob alloc] initWithEnvelopeData:envelopeData plaintextData:plaintextData]; - [job saveWithTransaction:transaction]; - }]; + OWSAssert(envelopeData); + OWSAssert(transaction); + + OWSMessageContentJob *job = + [[OWSMessageContentJob alloc] initWithEnvelopeData:envelopeData plaintextData:plaintextData]; + [job saveWithTransaction:transaction]; } - (void)removeJobsWithIds:(NSArray *)uniqueIds @@ -293,12 +295,15 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo return queue; } -- (void)enqueueEnvelopeData:(NSData *)envelopeData plaintextData:(NSData *_Nullable)plaintextData +- (void)enqueueEnvelopeData:(NSData *)envelopeData + plaintextData:(NSData *_Nullable)plaintextData + transaction:(YapDatabaseReadWriteTransaction *)transaction { OWSAssert(envelopeData); + OWSAssert(transaction); // We need to persist the decrypted envelope data ASAP to prevent data loss. - [self.finder addJobWithEnvelopeData:envelopeData plaintextData:plaintextData]; + [self.finder addJobWithEnvelopeData:envelopeData plaintextData:plaintextData transaction:transaction]; } - (void)drainQueue @@ -459,12 +464,15 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo [self.processingQueue drainQueue]; } -- (void)enqueueEnvelopeData:(NSData *)envelopeData plaintextData:(NSData *_Nullable)plaintextData +- (void)enqueueEnvelopeData:(NSData *)envelopeData + plaintextData:(NSData *_Nullable)plaintextData + transaction:(YapDatabaseReadWriteTransaction *)transaction { OWSAssert(envelopeData); + OWSAssert(transaction); // We need to persist the decrypted envelope data ASAP to prevent data loss. - [self.processingQueue enqueueEnvelopeData:envelopeData plaintextData:plaintextData]; + [self.processingQueue enqueueEnvelopeData:envelopeData plaintextData:plaintextData transaction:transaction]; [self.processingQueue drainQueue]; } diff --git a/SignalServiceKit/src/Messages/OWSIdentityManager.h b/SignalServiceKit/src/Messages/OWSIdentityManager.h index e727841f8..40863a0a2 100644 --- a/SignalServiceKit/src/Messages/OWSIdentityManager.h +++ b/SignalServiceKit/src/Messages/OWSIdentityManager.h @@ -30,6 +30,9 @@ extern const NSUInteger kIdentityKeyLength; - (void)generateNewIdentityKey; +// TODO: Rename to identityKeyForRecipientId. +- (nullable NSData *)identityKeyForRecipientIdWOT:(NSString *)recipientId; + - (nullable NSData *)identityKeyForRecipientId:(NSString *)recipientId protocolContext:(nullable id)protocolContext; - (void)setVerificationState:(OWSVerificationState)verificationState @@ -55,6 +58,7 @@ extern const NSUInteger kIdentityKeyLength; #pragma mark - Debug +// TODO: - (nullable ECKeyPair *)identityKeyPairWithoutProtocolContext; #if DEBUG diff --git a/SignalServiceKit/src/Messages/OWSIdentityManager.m b/SignalServiceKit/src/Messages/OWSIdentityManager.m index 210abed30..a1eca6f18 100644 --- a/SignalServiceKit/src/Messages/OWSIdentityManager.m +++ b/SignalServiceKit/src/Messages/OWSIdentityManager.m @@ -132,6 +132,15 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa inCollection:TSStorageManagerIdentityKeyStoreCollection]; } +- (nullable NSData *)identityKeyForRecipientIdWOT:(NSString *)recipientId +{ + __block NSData *_Nullable result = nil; + [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { + result = [self identityKeyForRecipientId:recipientId protocolContext:transaction]; + }]; + return result; +} + - (nullable NSData *)identityKeyForRecipientId:(NSString *)recipientId protocolContext:(nullable id)protocolContext { OWSAssert(recipientId.length > 0); diff --git a/SignalServiceKit/src/Messages/OWSMessageDecrypter.h b/SignalServiceKit/src/Messages/OWSMessageDecrypter.h index 8ac758a65..494e90021 100644 --- a/SignalServiceKit/src/Messages/OWSMessageDecrypter.h +++ b/SignalServiceKit/src/Messages/OWSMessageDecrypter.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSMessageHandler.h" @@ -7,8 +7,9 @@ NS_ASSUME_NONNULL_BEGIN @class OWSSignalServiceProtosEnvelope; +@class YapDatabaseReadWriteTransaction; -typedef void (^DecryptSuccessBlock)(NSData *_Nullable plaintextData); +typedef void (^DecryptSuccessBlock)(NSData *_Nullable plaintextData, YapDatabaseReadWriteTransaction *transaction); typedef void (^DecryptFailureBlock)(void); @interface OWSMessageDecrypter : OWSMessageHandler diff --git a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m index 267884999..8193b885f 100644 --- a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m +++ b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSMessageDecrypter.h" @@ -89,20 +89,18 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Decryption - (void)decryptEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - successBlock:(DecryptSuccessBlock)successBlockParameter + successBlock:(DecryptSuccessBlock)successBlock failureBlock:(DecryptFailureBlock)failureBlockParameter { OWSAssert(envelope); - OWSAssert(successBlockParameter); + OWSAssert(successBlock); OWSAssert(failureBlockParameter); OWSAssert([TSAccountManager isRegistered]); - // Ensure that successBlock and failureBlock are called on a worker queue. - DecryptSuccessBlock successBlock = ^(NSData *_Nullable plaintextData) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - successBlockParameter(plaintextData); - }); - }; + // successBlock is called synchronously so that we can avail ourselves of + // the transaction. + // + // Ensure that failureBlock is called on a worker queue. DecryptFailureBlock failureBlock = ^() { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ failureBlockParameter(); @@ -121,9 +119,9 @@ NS_ASSUME_NONNULL_BEGIN switch (envelope.type) { case OWSSignalServiceProtosEnvelopeTypeCiphertext: { [self decryptSecureMessage:envelope - successBlock:^(NSData *_Nullable plaintextData) { + successBlock:^(NSData *_Nullable plaintextData, YapDatabaseReadWriteTransaction *transaction) { DDLogDebug(@"%@ decrypted secure message.", self.logTag); - successBlock(plaintextData); + successBlock(plaintextData, transaction); } failureBlock:^(NSError *_Nullable error) { DDLogError(@"%@ decrypting secure message from address: %@ failed with error: %@", @@ -138,9 +136,9 @@ NS_ASSUME_NONNULL_BEGIN } case OWSSignalServiceProtosEnvelopeTypePrekeyBundle: { [self decryptPreKeyBundle:envelope - successBlock:^(NSData *_Nullable plaintextData) { + successBlock:^(NSData *_Nullable plaintextData, YapDatabaseReadWriteTransaction *transaction) { DDLogDebug(@"%@ decrypted pre-key whisper message", self.logTag); - successBlock(plaintextData); + successBlock(plaintextData, transaction); } failureBlock:^(NSError *_Nullable error) { DDLogError(@"%@ decrypting pre-key whisper message from address: %@ failed " @@ -157,10 +155,14 @@ NS_ASSUME_NONNULL_BEGIN // These message types don't have a payload to decrypt. case OWSSignalServiceProtosEnvelopeTypeReceipt: case OWSSignalServiceProtosEnvelopeTypeKeyExchange: - case OWSSignalServiceProtosEnvelopeTypeUnknown: - successBlock(nil); + case OWSSignalServiceProtosEnvelopeTypeUnknown: { + [TSStorageManager.protocolStoreDBConnection + readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + successBlock(nil, transaction); + }]; // Return to avoid double-acknowledging. return; + } default: DDLogWarn(@"Received unhandled envelope type: %d", (int)envelope.type); break; @@ -234,27 +236,30 @@ NS_ASSUME_NONNULL_BEGIN return; } - dispatch_async([OWSDispatch sessionStoreQueue], ^{ - @try { - id cipherMessage = cipherMessageBlock(encryptedData); - SessionCipher *cipher = [[SessionCipher alloc] initWithSessionStore:storageManager - preKeyStore:storageManager - signedPreKeyStore:storageManager - identityKeyStore:self.identityManager - recipientId:recipientId - deviceId:deviceId]; - - NSData *plaintextData = [[cipher decrypt:cipherMessage] removePadding]; - successBlock(plaintextData); - } @catch (NSException *exception) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self processException:exception envelope:envelope]; - NSString *errorDescription = [NSString - stringWithFormat:@"Exception while decrypting %@: %@", cipherTypeName, exception.description]; - NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorDescription); - failureBlock(error); - }); - } + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [TSStorageManager.protocolStoreDBConnection readWriteWithBlock:^( + YapDatabaseReadWriteTransaction *_Nonnull transaction) { + @try { + id cipherMessage = cipherMessageBlock(encryptedData); + SessionCipher *cipher = [[SessionCipher alloc] initWithSessionStore:storageManager + preKeyStore:storageManager + signedPreKeyStore:storageManager + identityKeyStore:self.identityManager + recipientId:recipientId + deviceId:deviceId]; + + NSData *plaintextData = [[cipher decrypt:cipherMessage protocolContext:transaction] removePadding]; + successBlock(plaintextData, transaction); + } @catch (NSException *exception) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self processException:exception envelope:envelope]; + NSString *errorDescription = [NSString + stringWithFormat:@"Exception while decrypting %@: %@", cipherTypeName, exception.description]; + NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorDescription); + failureBlock(error); + }); + } + }]; }); } diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.h b/SignalServiceKit/src/Messages/OWSMessageManager.h index b2d260409..723d04dd9 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.h +++ b/SignalServiceKit/src/Messages/OWSMessageManager.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSMessageHandler.h" @@ -20,10 +20,6 @@ NS_ASSUME_NONNULL_BEGIN plaintextData:(NSData *_Nullable)plaintextData transaction:(YapDatabaseReadWriteTransaction *)transaction; -- (NSUInteger)unreadMessagesCount; -- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread; -- (NSUInteger)unreadMessagesInThread:(TSThread *)thread; - @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index ba1dc25ed..467022ebb 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -21,6 +21,7 @@ #import "OWSIncomingMessageFinder.h" #import "OWSIncomingSentMessageTranscript.h" #import "OWSMessageSender.h" +#import "OWSMessageUtils.h" #import "OWSReadReceiptManager.h" #import "OWSRecordTranscriptJob.h" #import "OWSSyncConfigurationMessage.h" @@ -137,7 +138,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)yapDatabaseModified:(NSNotification *)notification { if (AppReadiness.isAppReady) { - [self updateApplicationBadgeCount]; + [OWSMessageUtils.sharedManager updateApplicationBadgeCount]; } } @@ -1118,47 +1119,6 @@ NS_ASSUME_NONNULL_BEGIN } } -- (NSUInteger)unreadMessagesCount -{ - __block NSUInteger numberOfItems; - [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInAllGroups]; - }]; - - return numberOfItems; -} - -- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread -{ - __block NSUInteger numberOfItems; - [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - id databaseView = [transaction ext:TSUnreadDatabaseViewExtensionName]; - OWSAssert(databaseView); - numberOfItems = ([databaseView numberOfItemsInAllGroups] - [databaseView numberOfItemsInGroup:thread.uniqueId]); - }]; - - return numberOfItems; -} - -- (void)updateApplicationBadgeCount -{ - if (!CurrentAppContext().isMainApp) { - return; - } - - NSUInteger numberOfItems = [self unreadMessagesCount]; - [CurrentAppContext() setMainAppBadgeNumber:numberOfItems]; -} - -- (NSUInteger)unreadMessagesInThread:(TSThread *)thread -{ - __block NSUInteger numberOfItems; - [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInGroup:thread.uniqueId]; - }]; - return numberOfItems; -} - @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/OWSMessageReceiver.m b/SignalServiceKit/src/Messages/OWSMessageReceiver.m index d5f44eb5c..e555049a7 100644 --- a/SignalServiceKit/src/Messages/OWSMessageReceiver.m +++ b/SignalServiceKit/src/Messages/OWSMessageReceiver.m @@ -331,11 +331,15 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin OWSSignalServiceProtosEnvelope *envelope = job.envelopeProto; [self.messageDecrypter decryptEnvelope:envelope - successBlock:^(NSData *_Nullable plaintextData) { - - // We can't decrypt the same message twice, so we need to persist - // the decrypted envelope data ASAP to prevent data loss. - [self.batchMessageProcessor enqueueEnvelopeData:job.envelopeData plaintextData:plaintextData]; + successBlock:^(NSData *_Nullable plaintextData, YapDatabaseReadWriteTransaction *transaction) { + OWSAssert(transaction); + + // We persist the decrypted envelope data in the same transaction within which + // it was decrypted to prevent data loss. If the new job isn't persisted, + // the session state side effects of its decryption are also rolled back. + [self.batchMessageProcessor enqueueEnvelopeData:job.envelopeData + plaintextData:plaintextData + transaction:transaction]; dispatch_async(self.serialQueue, ^{ completion(YES); diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index d4390266c..c34865b71 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -904,9 +904,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; NSData *newIdentityKey = [newIdentityKeyWithVersion removeKeyType]; - [[OWSIdentityManager sharedManager] saveRemoteIdentity:newIdentityKey - recipientId:recipient.recipientId - protocolContext:protocolContext]; + [TSStorageManager.protocolStoreDBConnection + readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { + [[OWSIdentityManager sharedManager] saveRemoteIdentity:newIdentityKey + recipientId:recipient.recipientId + protocolContext:transaction]; + }]; failureHandler(error); return; @@ -1101,30 +1104,34 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } } - dispatch_async([OWSDispatch sessionStoreQueue], ^{ - if (extraDevices.count < 1 && missingDevices.count < 1) { - OWSProdFail([OWSAnalyticsEvents messageSenderErrorNoMissingOrExtraDevices]); - } + [TSStorageManager.protocolStoreDBConnection + readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { + if (extraDevices.count < 1 && missingDevices.count < 1) { + OWSProdFail([OWSAnalyticsEvents messageSenderErrorNoMissingOrExtraDevices]); + } - if (extraDevices && extraDevices.count > 0) { - DDLogInfo(@"%@ removing extra devices: %@", self.logTag, extraDevices); - for (NSNumber *extraDeviceId in extraDevices) { - [self.storageManager deleteSessionForContact:recipient.uniqueId - deviceId:extraDeviceId.intValue - protocolContext:protocolContext]; + if (extraDevices && extraDevices.count > 0) { + DDLogInfo(@"%@ removing extra devices: %@", self.logTag, extraDevices); + for (NSNumber *extraDeviceId in extraDevices) { + [self.storageManager deleteSessionForContact:recipient.uniqueId + deviceId:extraDeviceId.intValue + protocolContext:transaction]; + } + + [recipient removeDevices:[NSSet setWithArray:extraDevices]]; } - [recipient removeDevices:[NSSet setWithArray:extraDevices]]; - } + if (missingDevices && missingDevices.count > 0) { + DDLogInfo(@"%@ Adding missing devices: %@", self.logTag, missingDevices); + [recipient addDevices:[NSSet setWithArray:missingDevices]]; + } - if (missingDevices && missingDevices.count > 0) { - DDLogInfo(@"%@ Adding missing devices: %@", self.logTag, missingDevices); - [recipient addDevices:[NSSet setWithArray:missingDevices]]; - } + [recipient saveWithTransaction:transaction]; - [recipient save]; - completionHandler(); - }); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + completionHandler(); + }); + }]; } - (void)handleMessageSentLocally:(TSOutgoingMessage *)message @@ -1249,19 +1256,19 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; @try { __block NSDictionary *messageDict; __block NSException *encryptionException; - // Mutating session state is not thread safe, so we operate on a serial queue, shared with decryption - // operations. - dispatch_sync([OWSDispatch sessionStoreQueue], ^{ - @try { - messageDict = [self encryptedMessageWithPlaintext:plainText - toRecipient:recipient.uniqueId - deviceId:deviceNumber - keyingStorage:self.storageManager - isSilent:message.isSilent]; - } @catch (NSException *exception) { - encryptionException = exception; - } - }); + [TSStorageManager.protocolStoreDBConnection + readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + @try { + messageDict = [self encryptedMessageWithPlaintext:plainText + toRecipient:recipient.uniqueId + deviceId:deviceNumber + keyingStorage:self.storageManager + isSilent:message.isSilent + transaction:transaction]; + } @catch (NSException *exception) { + encryptionException = exception; + } + }]; if (encryptionException) { DDLogInfo(@"%@ Exception during encryption: %@", self.logTag, encryptionException); @@ -1290,13 +1297,15 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; deviceId:(NSNumber *)deviceNumber keyingStorage:(TSStorageManager *)storage isSilent:(BOOL)isSilent + transaction:(YapDatabaseReadWriteTransaction *)transaction { OWSAssert(plainText); OWSAssert(identifier.length > 0); OWSAssert(deviceNumber); OWSAssert(storage); + OWSAssert(transaction); - if (![storage containsSession:identifier deviceId:[deviceNumber intValue] protocolContext:protocolContext]) { + if (![storage containsSession:identifier deviceId:[deviceNumber intValue] protocolContext:transaction]) { __block dispatch_semaphore_t sema = dispatch_semaphore_create(0); __block PreKeyBundle *_Nullable bundle; __block NSException *_Nullable exception; @@ -1341,10 +1350,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; recipientId:identifier deviceId:[deviceNumber intValue]]; @try { - // Mutating session state is not thread safe. - @synchronized(self) { - [builder processPrekeyBundle:bundle]; - } + [builder processPrekeyBundle:bundle protocolContext:transaction]; } @catch (NSException *exception) { if ([exception.name isEqualToString:UntrustedIdentityKeyException]) { OWSRaiseExceptionWithUserInfo(UntrustedIdentityKeyException, @@ -1363,18 +1369,20 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; recipientId:identifier deviceId:[deviceNumber intValue]]; - id encryptedMessage = [cipher encryptMessage:[plainText paddedMessageBody]]; + id encryptedMessage = + [cipher encryptMessage:[plainText paddedMessageBody] protocolContext:transaction]; NSData *serializedMessage = encryptedMessage.serialized; TSWhisperMessageType messageType = [self messageTypeForCipherMessage:encryptedMessage]; - OWSMessageServiceParams *messageParams = [[OWSMessageServiceParams alloc] initWithType:messageType - recipientId:identifier - device:[deviceNumber intValue] - content:serializedMessage - isSilent:isSilent - registrationId:cipher.remoteRegistrationId]; + OWSMessageServiceParams *messageParams = + [[OWSMessageServiceParams alloc] initWithType:messageType + recipientId:identifier + device:[deviceNumber intValue] + content:serializedMessage + isSilent:isSilent + registrationId:[cipher remoteRegistrationId:transaction]]; NSError *error; NSDictionary *jsonDict = [MTLJSONAdapter JSONDictionaryFromModel:messageParams error:&error]; @@ -1428,15 +1436,15 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; return; } - dispatch_async([OWSDispatch sessionStoreQueue], ^{ + [TSStorageManager.protocolStoreDBConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { for (NSUInteger i = 0; i < [devices count]; i++) { int deviceNumber = [devices[i] intValue]; [[TSStorageManager sharedManager] deleteSessionForContact:identifier deviceId:deviceNumber - protocolContext:protocolContext]; + protocolContext:transaction]; } - completionHandler(); - }); + }]; + completionHandler(); }); } diff --git a/SignalServiceKit/src/Messages/OWSMessageUtils.h b/SignalServiceKit/src/Messages/OWSMessageUtils.h index 1bc1ac2e1..2e2d05fe8 100644 --- a/SignalServiceKit/src/Messages/OWSMessageUtils.h +++ b/SignalServiceKit/src/Messages/OWSMessageUtils.h @@ -2,28 +2,21 @@ // Copyright (c) 2018 Open Whisper Systems. All rights reserved. // -#import "OWSMessageHandler.h" - NS_ASSUME_NONNULL_BEGIN -@class OWSSignalServiceProtosEnvelope; @class TSThread; -@class YapDatabaseReadWriteTransaction; -@interface OWSMessageManager : OWSMessageHandler +@interface OWSMessageUtils : NSObject - (instancetype)init NS_UNAVAILABLE; + (instancetype)sharedManager; -// processEnvelope: can be called from any thread. -- (void)processEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - plaintextData:(NSData *_Nullable)plaintextData - transaction:(YapDatabaseReadWriteTransaction *)transaction; - - (NSUInteger)unreadMessagesCount; - (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread; - (NSUInteger)unreadMessagesInThread:(TSThread *)thread; +- (void)updateApplicationBadgeCount; + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/OWSMessageUtils.m b/SignalServiceKit/src/Messages/OWSMessageUtils.m index 803ed3ba9..53cde3b5d 100644 --- a/SignalServiceKit/src/Messages/OWSMessageUtils.m +++ b/SignalServiceKit/src/Messages/OWSMessageUtils.m @@ -2,68 +2,28 @@ // Copyright (c) 2018 Open Whisper Systems. All rights reserved. // +#import "OWSMessageUtils.h" #import "AppContext.h" -#import "ContactsManagerProtocol.h" -#import "Cryptography.h" -#import "MimeTypeUtil.h" -#import "NSDate+OWS.h" -#import "NotificationsProtocol.h" -#import "OWSAttachmentsProcessor.h" -#import "OWSBlockingManager.h" -#import "OWSCallMessageHandler.h" -#import "OWSDevice.h" -#import "OWSDisappearingConfigurationUpdateInfoMessage.h" -#import "OWSDisappearingMessagesConfiguration.h" -#import "OWSDisappearingMessagesJob.h" -#import "OWSIdentityManager.h" -#import "OWSIncomingMessageFinder.h" -#import "OWSIncomingSentMessageTranscript.h" -#import "OWSMessageManager.h" -#import "OWSMessageSender.h" -#import "OWSReadReceiptManager.h" -#import "OWSRecordTranscriptJob.h" -#import "OWSSyncConfigurationMessage.h" -#import "OWSSyncContactsMessage.h" -#import "OWSSyncGroupsMessage.h" -#import "OWSSyncGroupsRequestMessage.h" -#import "ProfileManagerProtocol.h" -#import "TSAccountManager.h" -#import "TSContactThread.h" #import "TSDatabaseView.h" -#import "TSGroupModel.h" -#import "TSGroupThread.h" -#import "TSIncomingMessage.h" -#import "TSInfoMessage.h" -#import "TSNetworkManager.h" -#import "TSOutgoingMessage.h" -#import "TSStorageManager+SessionStore.h" #import "TSStorageManager.h" -#import "TextSecureKitEnv.h" +#import "TSThread.h" #import NS_ASSUME_NONNULL_BEGIN -@interface OWSMessageManager () +@interface OWSMessageUtils () -@property (nonatomic, readonly) id callMessageHandler; -@property (nonatomic, readonly) id contactsManager; -@property (nonatomic, readonly) TSStorageManager *storageManager; -@property (nonatomic, readonly) OWSMessageSender *messageSender; -@property (nonatomic, readonly) OWSIncomingMessageFinder *incomingMessageFinder; -@property (nonatomic, readonly) OWSBlockingManager *blockingManager; -@property (nonatomic, readonly) OWSIdentityManager *identityManager; -@property (nonatomic, readonly) TSNetworkManager *networkManager; @property (nonatomic, readonly) YapDatabaseConnection *dbConnection; @end #pragma mark - -@implementation OWSMessageManager +@implementation OWSMessageUtils + (instancetype)sharedManager { - static OWSMessageManager *sharedMyManager = nil; + static OWSMessageUtils *sharedMyManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedMyManager = [[self alloc] initDefault]; @@ -73,28 +33,12 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initDefault { - TSNetworkManager *networkManager = [TSNetworkManager sharedManager]; TSStorageManager *storageManager = [TSStorageManager sharedManager]; - id contactsManager = [TextSecureKitEnv sharedEnv].contactsManager; - id callMessageHandler = [TextSecureKitEnv sharedEnv].callMessageHandler; - OWSIdentityManager *identityManager = [OWSIdentityManager sharedManager]; - OWSMessageSender *messageSender = [TextSecureKitEnv sharedEnv].messageSender; - - return [self initWithNetworkManager:networkManager - storageManager:storageManager - callMessageHandler:callMessageHandler - contactsManager:contactsManager - identityManager:identityManager - messageSender:messageSender]; + return [self initWithStorageManager:storageManager]; } -- (instancetype)initWithNetworkManager:(TSNetworkManager *)networkManager - storageManager:(TSStorageManager *)storageManager - callMessageHandler:(id)callMessageHandler - contactsManager:(id)contactsManager - identityManager:(OWSIdentityManager *)identityManager - messageSender:(OWSMessageSender *)messageSender +- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager { self = [super init]; @@ -102,1006 +46,13 @@ NS_ASSUME_NONNULL_BEGIN return self; } - _storageManager = storageManager; - _networkManager = networkManager; - _callMessageHandler = callMessageHandler; - _contactsManager = contactsManager; - _identityManager = identityManager; - _messageSender = messageSender; - _dbConnection = storageManager.newDatabaseConnection; - _incomingMessageFinder = [[OWSIncomingMessageFinder alloc] initWithStorageManager:storageManager]; - _blockingManager = [OWSBlockingManager sharedManager]; OWSSingletonAssert(); - OWSAssert(CurrentAppContext().isMainApp); - - [self startObserving]; return self; } -- (void)startObserving -{ - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(yapDatabaseModified:) - name:YapDatabaseModifiedNotification - object:TSStorageManager.sharedManager.dbNotificationObject]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(yapDatabaseModified:) - name:YapDatabaseModifiedExternallyNotification - object:nil]; -} - -- (void)yapDatabaseModified:(NSNotification *)notification -{ - [self updateApplicationBadgeCount]; -} - -#pragma mark - Blocking - -- (BOOL)isEnvelopeBlocked:(OWSSignalServiceProtosEnvelope *)envelope -{ - OWSAssert(envelope); - - return [_blockingManager isRecipientIdBlocked:envelope.source]; -} - -#pragma mark - message handling - -- (void)processEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - plaintextData:(NSData *_Nullable)plaintextData - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(envelope); - OWSAssert(transaction); - OWSAssert([TSAccountManager isRegistered]); - OWSAssert(CurrentAppContext().isMainApp); - - DDLogInfo(@"%@ handling decrypted envelope: %@", self.logTag, [self descriptionForEnvelope:envelope]); - - OWSAssert(envelope.source.length > 0); - OWSAssert(![self isEnvelopeBlocked:envelope]); - - switch (envelope.type) { - case OWSSignalServiceProtosEnvelopeTypeCiphertext: - case OWSSignalServiceProtosEnvelopeTypePrekeyBundle: - if (plaintextData) { - [self handleEnvelope:envelope plaintextData:plaintextData transaction:transaction]; - } else { - OWSFail( - @"%@ missing decrypted data for envelope: %@", self.logTag, [self descriptionForEnvelope:envelope]); - } - break; - case OWSSignalServiceProtosEnvelopeTypeReceipt: - OWSAssert(!plaintextData); - [self handleDeliveryReceipt:envelope transaction:transaction]; - break; - // Other messages are just dismissed for now. - case OWSSignalServiceProtosEnvelopeTypeKeyExchange: - DDLogWarn(@"Received Key Exchange Message, not supported"); - break; - case OWSSignalServiceProtosEnvelopeTypeUnknown: - DDLogWarn(@"Received an unknown message type"); - break; - default: - DDLogWarn(@"Received unhandled envelope type: %d", (int)envelope.type); - break; - } -} - -- (void)handleDeliveryReceipt:(OWSSignalServiceProtosEnvelope *)envelope - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(envelope); - OWSAssert(transaction); - - // Old-style delivery notices don't include a "delivery timestamp". - [self processDeliveryReceiptsFromRecipientId:envelope.source - sentTimestamps:@[ - @(envelope.timestamp), - ] - deliveryTimestamp:nil - transaction:transaction]; -} - -// deliveryTimestamp is an optional parameter, since legacy -// delivery receipts don't have a "delivery timestamp". Those -// messages repurpose the "timestamp" field to indicate when the -// corresponding message was originally sent. -- (void)processDeliveryReceiptsFromRecipientId:(NSString *)recipientId - sentTimestamps:(NSArray *)sentTimestamps - deliveryTimestamp:(NSNumber *_Nullable)deliveryTimestamp - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(recipientId); - OWSAssert(sentTimestamps); - OWSAssert(transaction); - - for (NSNumber *nsTimestamp in sentTimestamps) { - uint64_t timestamp = [nsTimestamp unsignedLongLongValue]; - - NSArray *messages - = (NSArray *)[TSInteraction interactionsWithTimestamp:timestamp - ofClass:[TSOutgoingMessage class] - withTransaction:transaction]; - if (messages.count < 1) { - // The service sends delivery receipts for "unpersisted" messages - // like group updates, so these errors are expected to a certain extent. - // - // TODO: persist "early" delivery receipts. - DDLogInfo(@"%@ Missing message for delivery receipt: %llu", self.logTag, timestamp); - } else { - if (messages.count > 1) { - DDLogInfo(@"%@ More than one message (%zd) for delivery receipt: %llu", - self.logTag, - messages.count, - timestamp); - } - for (TSOutgoingMessage *outgoingMessage in messages) { - [outgoingMessage updateWithDeliveredToRecipientId:recipientId - deliveryTimestamp:deliveryTimestamp - transaction:transaction]; - } - } - } -} - -- (void)handleEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - plaintextData:(NSData *)plaintextData - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(envelope); - OWSAssert(plaintextData); - OWSAssert(transaction); - OWSAssert(envelope.hasTimestamp && envelope.timestamp > 0); - OWSAssert(envelope.hasSource && envelope.source.length > 0); - OWSAssert(envelope.hasSourceDevice && envelope.sourceDevice > 0); - - BOOL duplicateEnvelope = [self.incomingMessageFinder existsMessageWithTimestamp:envelope.timestamp - sourceId:envelope.source - sourceDeviceId:envelope.sourceDevice - transaction:transaction]; - if (duplicateEnvelope) { - DDLogInfo(@"%@ Ignoring previously received envelope from %@ with timestamp: %llu", - self.logTag, - envelopeAddress(envelope), - envelope.timestamp); - return; - } - - if (envelope.hasContent) { - OWSSignalServiceProtosContent *content = [OWSSignalServiceProtosContent parseFromData:plaintextData]; - DDLogInfo(@"%@ handling content: ", self.logTag, [self descriptionForContent:content]); - - if (content.hasSyncMessage) { - [self handleIncomingEnvelope:envelope withSyncMessage:content.syncMessage transaction:transaction]; - - [[OWSDeviceManager sharedManager] setHasReceivedSyncMessage]; - } else if (content.hasDataMessage) { - [self handleIncomingEnvelope:envelope withDataMessage:content.dataMessage transaction:transaction]; - } else if (content.hasCallMessage) { - [self handleIncomingEnvelope:envelope withCallMessage:content.callMessage]; - } else if (content.hasNullMessage) { - DDLogInfo(@"%@ Received null message.", self.logTag); - } else if (content.hasReceiptMessage) { - [self handleIncomingEnvelope:envelope withReceiptMessage:content.receiptMessage transaction:transaction]; - } else { - DDLogWarn(@"%@ Ignoring envelope. Content with no known payload", self.logTag); - } - } else if (envelope.hasLegacyMessage) { // DEPRECATED - Remove after all clients have been upgraded. - OWSSignalServiceProtosDataMessage *dataMessage = - [OWSSignalServiceProtosDataMessage parseFromData:plaintextData]; - DDLogInfo( - @"%@ handling message: ", self.logTag, [self descriptionForDataMessage:dataMessage]); - - [self handleIncomingEnvelope:envelope withDataMessage:dataMessage transaction:transaction]; - } else { - OWSProdInfoWEnvelope([OWSAnalyticsEvents messageManagerErrorEnvelopeNoActionablePayload], envelope); - } -} - -- (void)handleIncomingEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - withDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(envelope); - OWSAssert(dataMessage); - OWSAssert(transaction); - - if ([dataMessage hasProfileKey]) { - NSData *profileKey = [dataMessage profileKey]; - NSString *recipientId = envelope.source; - if (profileKey.length == kAES256_KeyByteLength) { - [self.profileManager setProfileKeyData:profileKey forRecipientId:recipientId]; - } else { - OWSFail( - @"Unexpected profile key length:%lu on message from:%@", (unsigned long)profileKey.length, recipientId); - } - } - - if (dataMessage.hasGroup) { - TSGroupThread *_Nullable groupThread = - [TSGroupThread threadWithGroupId:dataMessage.group.id transaction:transaction]; - - if (!groupThread) { - // Unknown group. - if (dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate) { - // Accept group updates for unknown groups. - } else if (dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeDeliver) { - [self sendGroupInfoRequest:dataMessage.group.id envelope:envelope transaction:transaction]; - return; - } else { - DDLogInfo(@"%@ Ignoring group message for unknown group from: %@", self.logTag, envelope.source); - return; - } - } - } - - if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsEndSession) != 0) { - [self handleEndSessionMessageWithEnvelope:envelope dataMessage:dataMessage transaction:transaction]; - } else if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate) != 0) { - [self handleExpirationTimerUpdateMessageWithEnvelope:envelope dataMessage:dataMessage transaction:transaction]; - } else if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsProfileKeyUpdate) != 0) { - [self handleProfileKeyMessageWithEnvelope:envelope dataMessage:dataMessage]; - } else if (dataMessage.attachments.count > 0) { - [self handleReceivedMediaWithEnvelope:envelope dataMessage:dataMessage transaction:transaction]; - } else { - [self handleReceivedTextMessageWithEnvelope:envelope dataMessage:dataMessage transaction:transaction]; - - if ([self isDataMessageGroupAvatarUpdate:dataMessage]) { - DDLogVerbose(@"%@ Data message had group avatar attachment", self.logTag); - [self handleReceivedGroupAvatarUpdateWithEnvelope:envelope dataMessage:dataMessage transaction:transaction]; - } - } -} - -- (void)sendGroupInfoRequest:(NSData *)groupId - envelope:(OWSSignalServiceProtosEnvelope *)envelope - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(groupId.length > 0); - OWSAssert(envelope); - OWSAssert(transaction); - - if (groupId.length < 1) { - return; - } - - // FIXME: https://github.com/WhisperSystems/Signal-iOS/issues/1340 - DDLogInfo(@"%@ Sending group info request: %@", self.logTag, envelopeAddress(envelope)); - - NSString *recipientId = envelope.source; - - TSThread *thread = [TSContactThread getOrCreateThreadWithContactId:recipientId transaction:transaction]; - - OWSSyncGroupsRequestMessage *syncGroupsRequestMessage = - [[OWSSyncGroupsRequestMessage alloc] initWithThread:thread groupId:groupId]; - [self.messageSender enqueueMessage:syncGroupsRequestMessage - success:^{ - DDLogWarn(@"%@ Successfully sent Request Group Info message.", self.logTag); - } - failure:^(NSError *error) { - DDLogError(@"%@ Failed to send Request Group Info message with error: %@", self.logTag, error); - }]; -} - -- (id)profileManager -{ - return [TextSecureKitEnv sharedEnv].profileManager; -} - -- (void)handleIncomingEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - withReceiptMessage:(OWSSignalServiceProtosReceiptMessage *)receiptMessage - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(envelope); - OWSAssert(receiptMessage); - OWSAssert(transaction); - - PBArray *messageTimestamps = receiptMessage.timestamp; - NSMutableArray *sentTimestamps = [NSMutableArray new]; - for (int i = 0; i < messageTimestamps.count; i++) { - UInt64 timestamp = [messageTimestamps uint64AtIndex:i]; - [sentTimestamps addObject:@(timestamp)]; - } - - switch (receiptMessage.type) { - case OWSSignalServiceProtosReceiptMessageTypeDelivery: - DDLogVerbose(@"%@ Processing receipt message with delivery receipts.", self.logTag); - [self processDeliveryReceiptsFromRecipientId:envelope.source - sentTimestamps:sentTimestamps - deliveryTimestamp:@(envelope.timestamp) - transaction:transaction]; - return; - case OWSSignalServiceProtosReceiptMessageTypeRead: - DDLogVerbose(@"%@ Processing receipt message with read receipts.", self.logTag); - [OWSReadReceiptManager.sharedManager processReadReceiptsFromRecipientId:envelope.source - sentTimestamps:sentTimestamps - readTimestamp:envelope.timestamp]; - break; - default: - DDLogInfo(@"%@ Ignoring receipt message of unknown type: %d.", self.logTag, (int)receiptMessage.type); - return; - } -} - -- (void)handleIncomingEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - withCallMessage:(OWSSignalServiceProtosCallMessage *)callMessage -{ - OWSAssert(envelope); - OWSAssert(callMessage); - - if ([callMessage hasProfileKey]) { - NSData *profileKey = [callMessage profileKey]; - NSString *recipientId = envelope.source; - [self.profileManager setProfileKeyData:profileKey forRecipientId:recipientId]; - } - - // By dispatching async, we introduce the possibility that these messages might be lost - // if the app exits before this block is executed. This is fine, since the call by - // definition will end if the app exits. - dispatch_async(dispatch_get_main_queue(), ^{ - if (callMessage.hasOffer) { - [self.callMessageHandler receivedOffer:callMessage.offer fromCallerId:envelope.source]; - } else if (callMessage.hasAnswer) { - [self.callMessageHandler receivedAnswer:callMessage.answer fromCallerId:envelope.source]; - } else if (callMessage.iceUpdate.count > 0) { - for (OWSSignalServiceProtosCallMessageIceUpdate *iceUpdate in callMessage.iceUpdate) { - [self.callMessageHandler receivedIceUpdate:iceUpdate fromCallerId:envelope.source]; - } - } else if (callMessage.hasHangup) { - DDLogVerbose(@"%@ Received CallMessage with Hangup.", self.logTag); - [self.callMessageHandler receivedHangup:callMessage.hangup fromCallerId:envelope.source]; - } else if (callMessage.hasBusy) { - [self.callMessageHandler receivedBusy:callMessage.busy fromCallerId:envelope.source]; - } else { - OWSProdInfoWEnvelope([OWSAnalyticsEvents messageManagerErrorCallMessageNoActionablePayload], envelope); - } - }); -} - -- (void)handleReceivedGroupAvatarUpdateWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(envelope); - OWSAssert(dataMessage); - OWSAssert(transaction); - - TSGroupThread *_Nullable groupThread = - [TSGroupThread threadWithGroupId:dataMessage.group.id transaction:transaction]; - if (!groupThread) { - OWSFail(@"%@ Missing group for group avatar update", self.logTag); - return; - } - - OWSAssert(groupThread); - OWSAttachmentsProcessor *attachmentsProcessor = - [[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:@[ dataMessage.group.avatar ] - timestamp:envelope.timestamp - relay:envelope.relay - thread:groupThread - networkManager:self.networkManager - storageManager:self.storageManager - transaction:transaction]; - - if (!attachmentsProcessor.hasSupportedAttachments) { - DDLogWarn(@"%@ received unsupported group avatar envelope", self.logTag); - return; - } - [attachmentsProcessor fetchAttachmentsForMessage:nil - transaction:transaction - success:^(TSAttachmentStream *attachmentStream) { - [groupThread updateAvatarWithAttachmentStream:attachmentStream]; - } - failure:^(NSError *error) { - DDLogError(@"%@ failed to fetch attachments for group avatar sent at: %llu. with error: %@", - self.logTag, - envelope.timestamp, - error); - }]; -} - -- (void)handleReceivedMediaWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(envelope); - OWSAssert(dataMessage); - OWSAssert(transaction); - - TSThread *_Nullable thread = [self threadForEnvelope:envelope dataMessage:dataMessage transaction:transaction]; - if (!thread) { - OWSFail(@"%@ ignoring media message for unknown group.", self.logTag); - return; - } - - OWSAttachmentsProcessor *attachmentsProcessor = - [[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:dataMessage.attachments - timestamp:envelope.timestamp - relay:envelope.relay - thread:thread - networkManager:self.networkManager - storageManager:self.storageManager - transaction:transaction]; - if (!attachmentsProcessor.hasSupportedAttachments) { - DDLogWarn(@"%@ received unsupported media envelope", self.logTag); - return; - } - - TSIncomingMessage *_Nullable createdMessage = - [self handleReceivedEnvelope:envelope - withDataMessage:dataMessage - attachmentIds:attachmentsProcessor.supportedAttachmentIds - transaction:transaction]; - - if (!createdMessage) { - return; - } - - DDLogDebug(@"%@ incoming attachment message: %@", self.logTag, createdMessage.debugDescription); - - [attachmentsProcessor fetchAttachmentsForMessage:createdMessage - transaction:transaction - success:^(TSAttachmentStream *attachmentStream) { - DDLogDebug(@"%@ successfully fetched attachment: %@ for message: %@", - self.logTag, - attachmentStream, - createdMessage); - } - failure:^(NSError *error) { - DDLogError( - @"%@ failed to fetch attachments for message: %@ with error: %@", self.logTag, createdMessage, error); - }]; -} - -- (void)handleIncomingEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - withSyncMessage:(OWSSignalServiceProtosSyncMessage *)syncMessage - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(envelope); - OWSAssert(syncMessage); - OWSAssert(transaction); - OWSAssert([TSAccountManager isRegistered]); - - NSString *localNumber = [TSAccountManager localNumber]; - if (![localNumber isEqualToString:envelope.source]) { - // Sync messages should only come from linked devices. - OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorSyncMessageFromUnknownSource], envelope); - return; - } - - if (syncMessage.hasSent) { - OWSIncomingSentMessageTranscript *transcript = - [[OWSIncomingSentMessageTranscript alloc] initWithProto:syncMessage.sent relay:envelope.relay]; - - OWSRecordTranscriptJob *recordJob = - [[OWSRecordTranscriptJob alloc] initWithIncomingSentMessageTranscript:transcript]; - - OWSSignalServiceProtosDataMessage *dataMessage = syncMessage.sent.message; - OWSAssert(dataMessage); - NSString *destination = syncMessage.sent.destination; - if (dataMessage && destination.length > 0 && dataMessage.hasProfileKey) { - // If we observe a linked device sending our profile key to another - // user, we can infer that that user belongs in our profile whitelist. - if (dataMessage.hasGroup) { - [self.profileManager addGroupIdToProfileWhitelist:dataMessage.group.id]; - } else { - [self.profileManager addUserToProfileWhitelist:destination]; - } - } - - if ([self isDataMessageGroupAvatarUpdate:syncMessage.sent.message]) { - [recordJob runWithAttachmentHandler:^(TSAttachmentStream *attachmentStream) { - [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - TSGroupThread *_Nullable groupThread = - [TSGroupThread threadWithGroupId:dataMessage.group.id transaction:transaction]; - if (!groupThread) { - OWSFail(@"%@ ignoring sync group avatar update for unknown group.", self.logTag); - return; - } - - [groupThread updateAvatarWithAttachmentStream:attachmentStream transaction:transaction]; - }]; - } - transaction:transaction]; - } else { - [recordJob runWithAttachmentHandler:^(TSAttachmentStream *attachmentStream) { - DDLogDebug(@"%@ successfully fetched transcript attachment: %@", self.logTag, attachmentStream); - } - transaction:transaction]; - } - } else if (syncMessage.hasRequest) { - if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeContacts) { - // We respond asynchronously because populating the sync message will - // create transactions and it's not practical (due to locking in the OWSIdentityManager) - // to plumb our transaction through. - // - // In rare cases this means we won't respond to the sync request, but that's - // acceptable. - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - OWSSyncContactsMessage *syncContactsMessage = - [[OWSSyncContactsMessage alloc] initWithSignalAccounts:self.contactsManager.signalAccounts - identityManager:self.identityManager - profileManager:self.profileManager]; - DataSource *dataSource = - [DataSourceValue dataSourceWithSyncMessage:[syncContactsMessage buildPlainTextAttachmentData]]; - [self.messageSender enqueueTemporaryAttachment:dataSource - contentType:OWSMimeTypeApplicationOctetStream - inMessage:syncContactsMessage - success:^{ - DDLogInfo(@"%@ Successfully sent Contacts response syncMessage.", self.logTag); - } - failure:^(NSError *error) { - DDLogError( - @"%@ Failed to send Contacts response syncMessage with error: %@", self.logTag, error); - }]; - }); - } else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeGroups) { - OWSSyncGroupsMessage *syncGroupsMessage = [[OWSSyncGroupsMessage alloc] init]; - DataSource *dataSource = [DataSourceValue - dataSourceWithSyncMessage:[syncGroupsMessage buildPlainTextAttachmentDataWithTransaction:transaction]]; - [self.messageSender enqueueTemporaryAttachment:dataSource - contentType:OWSMimeTypeApplicationOctetStream - inMessage:syncGroupsMessage - success:^{ - DDLogInfo(@"%@ Successfully sent Groups response syncMessage.", self.logTag); - } - failure:^(NSError *error) { - DDLogError(@"%@ Failed to send Groups response syncMessage with error: %@", self.logTag, error); - }]; - } else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeBlocked) { - DDLogInfo(@"%@ Received request for block list", self.logTag); - [_blockingManager syncBlockedPhoneNumbers]; - } else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeConfiguration) { - BOOL areReadReceiptsEnabled = - [[OWSReadReceiptManager sharedManager] areReadReceiptsEnabledWithTransaction:transaction]; - OWSSyncConfigurationMessage *syncConfigurationMessage = - [[OWSSyncConfigurationMessage alloc] initWithReadReceiptsEnabled:areReadReceiptsEnabled]; - [self.messageSender enqueueMessage:syncConfigurationMessage - success:^{ - DDLogInfo(@"%@ Successfully sent Configuration response syncMessage.", self.logTag); - } - failure:^(NSError *error) { - DDLogError( - @"%@ Failed to send Configuration response syncMessage with error: %@", self.logTag, error); - }]; - } else { - DDLogWarn(@"%@ ignoring unsupported sync request message", self.logTag); - } - } else if (syncMessage.hasBlocked) { - NSArray *blockedPhoneNumbers = [syncMessage.blocked.numbers copy]; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [_blockingManager setBlockedPhoneNumbers:blockedPhoneNumbers sendSyncMessage:NO]; - }); - } else if (syncMessage.read.count > 0) { - DDLogInfo(@"%@ Received %ld read receipt(s)", self.logTag, (u_long)syncMessage.read.count); - - [OWSReadReceiptManager.sharedManager processReadReceiptsFromLinkedDevice:syncMessage.read - transaction:transaction]; - } else if (syncMessage.hasVerified) { - DDLogInfo(@"%@ Received verification state for %@", self.logTag, syncMessage.verified.destination); - [self.identityManager processIncomingSyncMessage:syncMessage.verified]; - } else { - DDLogWarn(@"%@ Ignoring unsupported sync message.", self.logTag); - } -} - -- (void)handleEndSessionMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(envelope); - OWSAssert(dataMessage); - OWSAssert(transaction); - - TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction]; - - [[[TSInfoMessage alloc] initWithTimestamp:envelope.timestamp - inThread:thread - messageType:TSInfoMessageTypeSessionDidEnd] saveWithTransaction:transaction]; - - [self.storageManager deleteAllSessionsForContact:envelope.source protocolContext:transaction]; -} - -- (void)handleExpirationTimerUpdateMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(envelope); - OWSAssert(dataMessage); - OWSAssert(transaction); - - TSThread *_Nullable thread = [self threadForEnvelope:envelope dataMessage:dataMessage transaction:transaction]; - if (!thread) { - OWSFail(@"%@ ignoring expiring messages update for unknown group.", self.logTag); - return; - } - - OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration; - if (dataMessage.hasExpireTimer && dataMessage.expireTimer > 0) { - DDLogInfo(@"%@ Expiring messages duration turned to %u for thread %@", - self.logTag, - (unsigned int)dataMessage.expireTimer, - thread); - disappearingMessagesConfiguration = - [[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:thread.uniqueId - enabled:YES - durationSeconds:dataMessage.expireTimer]; - } else { - DDLogInfo(@"%@ Expiring messages have been turned off for thread %@", self.logTag, thread); - disappearingMessagesConfiguration = [[OWSDisappearingMessagesConfiguration alloc] - initWithThreadId:thread.uniqueId - enabled:NO - durationSeconds:OWSDisappearingMessagesConfigurationDefaultExpirationDuration]; - } - OWSAssert(disappearingMessagesConfiguration); - [disappearingMessagesConfiguration saveWithTransaction:transaction]; - NSString *name = [self.contactsManager displayNameForPhoneIdentifier:envelope.source]; - OWSDisappearingConfigurationUpdateInfoMessage *message = - [[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:envelope.timestamp - thread:thread - configuration:disappearingMessagesConfiguration - createdByRemoteName:name]; - [message saveWithTransaction:transaction]; -} - -- (void)handleProfileKeyMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage -{ - OWSAssert(envelope); - OWSAssert(dataMessage); - - NSString *recipientId = envelope.source; - if (!dataMessage.hasProfileKey) { - OWSFail( - @"%@ received profile key message without profile key from: %@", self.logTag, envelopeAddress(envelope)); - return; - } - NSData *profileKey = dataMessage.profileKey; - if (profileKey.length != kAES256_KeyByteLength) { - OWSFail(@"%@ received profile key of unexpected length:%lu from:%@", - self.logTag, - (unsigned long)profileKey.length, - envelopeAddress(envelope)); - return; - } - - id profileManager = [TextSecureKitEnv sharedEnv].profileManager; - [profileManager setProfileKeyData:profileKey forRecipientId:recipientId]; -} - -- (void)handleReceivedTextMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(envelope); - OWSAssert(dataMessage); - OWSAssert(transaction); - - [self handleReceivedEnvelope:envelope withDataMessage:dataMessage attachmentIds:@[] transaction:transaction]; -} - -- (void)sendGroupUpdateForThread:(TSGroupThread *)gThread message:(TSOutgoingMessage *)message -{ - OWSAssert(gThread); - OWSAssert(gThread.groupModel); - OWSAssert(message); - - if (gThread.groupModel.groupImage) { - NSData *data = UIImagePNGRepresentation(gThread.groupModel.groupImage); - DataSource *_Nullable dataSource = [DataSourceValue dataSourceWithData:data fileExtension:@"png"]; - [self.messageSender enqueueAttachment:dataSource - contentType:OWSMimeTypeImagePng - sourceFilename:nil - inMessage:message - success:^{ - DDLogDebug(@"%@ Successfully sent group update with avatar", self.logTag); - } - failure:^(NSError *error) { - DDLogError(@"%@ Failed to send group avatar update with error: %@", self.logTag, error); - }]; - } else { - [self.messageSender enqueueMessage:message - success:^{ - DDLogDebug(@"%@ Successfully sent group update", self.logTag); - } - failure:^(NSError *error) { - DDLogError(@"%@ Failed to send group update with error: %@", self.logTag, error); - }]; - } -} - -- (void)handleGroupInfoRequest:(OWSSignalServiceProtosEnvelope *)envelope - dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(envelope); - OWSAssert(dataMessage); - OWSAssert(transaction); - OWSAssert(dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeRequestInfo); - - NSData *groupId = dataMessage.hasGroup ? dataMessage.group.id : nil; - if (!groupId) { - OWSFail(@"Group info request is missing group id."); - return; - } - - DDLogWarn( - @"%@ Received 'Request Group Info' message for group: %@ from: %@", self.logTag, groupId, envelope.source); - - TSGroupThread *_Nullable gThread = [TSGroupThread threadWithGroupId:dataMessage.group.id transaction:transaction]; - if (!gThread) { - DDLogWarn(@"%@ Unknown group: %@", self.logTag, groupId); - return; - } - - // Ensure sender is in the group. - if (![gThread.groupModel.groupMemberIds containsObject:envelope.source]) { - DDLogWarn(@"%@ Ignoring 'Request Group Info' message for non-member of group. %@ not in %@", - self.logTag, - envelope.source, - gThread.groupModel.groupMemberIds); - return; - } - - // Ensure we are in the group. - OWSAssert([TSAccountManager isRegistered]); - NSString *localNumber = [TSAccountManager localNumber]; - if (![gThread.groupModel.groupMemberIds containsObject:localNumber]) { - DDLogWarn(@"%@ Ignoring 'Request Group Info' message for group we no longer belong to.", self.logTag); - return; - } - - NSString *updateGroupInfo = - [gThread.groupModel getInfoStringAboutUpdateTo:gThread.groupModel contactsManager:self.contactsManager]; - TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] - inThread:gThread - groupMetaMessage:TSGroupMessageUpdate]; - [message updateWithCustomMessage:updateGroupInfo transaction:transaction]; - // Only send this group update to the requester. - [message updateWithSingleGroupRecipient:envelope.source transaction:transaction]; - - [self sendGroupUpdateForThread:gThread message:message]; -} - -- (TSIncomingMessage *_Nullable)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - withDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage - attachmentIds:(NSArray *)attachmentIds - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(envelope); - OWSAssert(dataMessage); - OWSAssert(transaction); - - uint64_t timestamp = envelope.timestamp; - NSString *body = dataMessage.body; - NSData *groupId = dataMessage.hasGroup ? dataMessage.group.id : nil; - - if (dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeRequestInfo) { - [self handleGroupInfoRequest:envelope dataMessage:dataMessage transaction:transaction]; - return nil; - } - - if (groupId.length > 0) { - NSMutableSet *newMemberIds = [NSMutableSet setWithArray:dataMessage.group.members]; - - // Group messages create the group if it doesn't already exist. - // - // We distinguish between the old group state (if any) and the new group state. - TSGroupThread *_Nullable oldGroupThread = [TSGroupThread threadWithGroupId:groupId transaction:transaction]; - if (oldGroupThread) { - // Don't trust other clients; ensure all known group members remain in the - // group unless it is a "quit" message in which case we should only remove - // the quiting member below. - [newMemberIds addObjectsFromArray:oldGroupThread.groupModel.groupMemberIds]; - } - - switch (dataMessage.group.type) { - case OWSSignalServiceProtosGroupContextTypeUpdate: { - // Ensures that the thread exists but doesn't update it. - TSGroupThread *newGroupThread = - [TSGroupThread getOrCreateThreadWithGroupId:groupId transaction:transaction]; - - TSGroupModel *newGroupModel = [[TSGroupModel alloc] initWithTitle:dataMessage.group.name - memberIds:[newMemberIds.allObjects mutableCopy] - image:oldGroupThread.groupModel.groupImage - groupId:dataMessage.group.id]; - NSString *updateGroupInfo = [newGroupThread.groupModel getInfoStringAboutUpdateTo:newGroupModel - contactsManager:self.contactsManager]; - newGroupThread.groupModel = newGroupModel; - [newGroupThread saveWithTransaction:transaction]; - - [[[TSInfoMessage alloc] initWithTimestamp:timestamp - inThread:newGroupThread - messageType:TSInfoMessageTypeGroupUpdate - customMessage:updateGroupInfo] saveWithTransaction:transaction]; - return nil; - } - case OWSSignalServiceProtosGroupContextTypeQuit: { - if (!oldGroupThread) { - DDLogInfo(@"%@ ignoring quit group message from unknown group.", self.logTag); - return nil; - } - [newMemberIds removeObject:envelope.source]; - oldGroupThread.groupModel.groupMemberIds = [newMemberIds.allObjects mutableCopy]; - [oldGroupThread saveWithTransaction:transaction]; - - NSString *nameString = [self.contactsManager displayNameForPhoneIdentifier:envelope.source]; - NSString *updateGroupInfo = - [NSString stringWithFormat:NSLocalizedString(@"GROUP_MEMBER_LEFT", @""), nameString]; - [[[TSInfoMessage alloc] initWithTimestamp:timestamp - inThread:oldGroupThread - messageType:TSInfoMessageTypeGroupUpdate - customMessage:updateGroupInfo] saveWithTransaction:transaction]; - return nil; - } - case OWSSignalServiceProtosGroupContextTypeDeliver: { - if (!oldGroupThread) { - OWSFail(@"%@ ignoring deliver group message from unknown group.", self.logTag); - return nil; - } - - if (body.length == 0 && attachmentIds.count < 1) { - DDLogWarn(@"%@ ignoring empty incoming message from: %@ for group: %@ with timestamp: %lu", - self.logTag, - envelopeAddress(envelope), - groupId, - (unsigned long)timestamp); - return nil; - } - - DDLogDebug(@"%@ incoming message from: %@ for group: %@ with timestamp: %lu", - self.logTag, - envelopeAddress(envelope), - groupId, - (unsigned long)timestamp); - TSIncomingMessage *incomingMessage = - [[TSIncomingMessage alloc] initWithTimestamp:timestamp - inThread:oldGroupThread - authorId:envelope.source - sourceDeviceId:envelope.sourceDevice - messageBody:body - attachmentIds:attachmentIds - expiresInSeconds:dataMessage.expireTimer]; - [self finalizeIncomingMessage:incomingMessage - thread:oldGroupThread - envelope:envelope - dataMessage:dataMessage - attachmentIds:attachmentIds - transaction:transaction]; - return incomingMessage; - } - default: { - DDLogWarn(@"%@ Ignoring unknown group message type: %d", self.logTag, (int)dataMessage.group.type); - return nil; - } - } - } else { - if (body.length == 0 && attachmentIds.count < 1) { - DDLogWarn(@"%@ ignoring empty incoming message from: %@ with timestamp: %lu", - self.logTag, - envelopeAddress(envelope), - (unsigned long)timestamp); - return nil; - } - - DDLogDebug(@"%@ incoming message from: %@ with timestamp: %lu", - self.logTag, - envelopeAddress(envelope), - (unsigned long)timestamp); - TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source - transaction:transaction - relay:envelope.relay]; - - TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timestamp - inThread:thread - authorId:[thread contactIdentifier] - sourceDeviceId:envelope.sourceDevice - messageBody:body - attachmentIds:attachmentIds - expiresInSeconds:dataMessage.expireTimer]; - [self finalizeIncomingMessage:incomingMessage - thread:thread - envelope:envelope - dataMessage:dataMessage - attachmentIds:attachmentIds - transaction:transaction]; - return incomingMessage; - } -} - -- (void)finalizeIncomingMessage:(TSIncomingMessage *)incomingMessage - thread:(TSThread *)thread - envelope:(OWSSignalServiceProtosEnvelope *)envelope - dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage - attachmentIds:(NSArray *)attachmentIds - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(thread); - OWSAssert(incomingMessage); - OWSAssert(envelope); - OWSAssert(dataMessage); - OWSAssert(attachmentIds); - OWSAssert(transaction); - - OWSAssert([TSAccountManager isRegistered]); - NSString *localNumber = [TSAccountManager localNumber]; - NSString *body = dataMessage.body; - uint64_t timestamp = envelope.timestamp; - - if (!thread) { - OWSFail(@"%@ Can't finalize without thread", self.logTag); - return; - } - if (!incomingMessage) { - OWSFail(@"%@ Can't finalize missing message", self.logTag); - return; - } - - [incomingMessage saveWithTransaction:transaction]; - - // Any messages sent from the current user - from this device or another - should be - // automatically marked as read. - BOOL shouldMarkMessageAsRead = [envelope.source isEqualToString:localNumber]; - if (shouldMarkMessageAsRead) { - // Don't send a read receipt for messages sent by ourselves. - [incomingMessage markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:YES]; - } - - DDLogDebug(@"%@ shouldMarkMessageAsRead: %d (%@)", self.logTag, shouldMarkMessageAsRead, envelope.source); - - // In case we already have a read receipt for this new message (this happens sometimes). - [OWSReadReceiptManager.sharedManager applyEarlyReadReceiptsForIncomingMessage:incomingMessage - transaction:transaction]; - - [OWSDisappearingMessagesJob becomeConsistentWithConfigurationForMessage:incomingMessage - contactsManager:self.contactsManager]; - - // Update thread preview in inbox - [thread touchWithTransaction:transaction]; - - [[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForIncomingMessage:incomingMessage - inThread:thread - contactsManager:self.contactsManager - transaction:transaction]; -} - -#pragma mark - helpers - -- (BOOL)isDataMessageGroupAvatarUpdate:(OWSSignalServiceProtosDataMessage *)dataMessage -{ - return dataMessage.hasGroup && dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate - && dataMessage.group.hasAvatar; -} - -/** - * @returns - * Group or Contact thread for message, creating a new contact thread if necessary, - * but never creating a new group thread. - */ -- (nullable TSThread *)threadForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(envelope); - OWSAssert(dataMessage); - OWSAssert(transaction); - - if (dataMessage.hasGroup) { - NSData *groupId = dataMessage.group.id; - OWSAssert(groupId.length > 0); - TSGroupThread *_Nullable groupThread = [TSGroupThread threadWithGroupId:groupId transaction:transaction]; - // This method should only be called from a code path that has already verified - // that this is a "known" group. - OWSAssert(groupThread); - return groupThread; - } else { - return [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction]; - } -} - - (NSUInteger)unreadMessagesCount { __block NSUInteger numberOfItems; diff --git a/SignalServiceKit/src/Security/OWSFingerprintBuilder.m b/SignalServiceKit/src/Security/OWSFingerprintBuilder.m index b3bfdee84..303ed13ad 100644 --- a/SignalServiceKit/src/Security/OWSFingerprintBuilder.m +++ b/SignalServiceKit/src/Security/OWSFingerprintBuilder.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSFingerprintBuilder.h" @@ -36,7 +36,8 @@ NS_ASSUME_NONNULL_BEGIN - (nullable OWSFingerprint *)fingerprintWithTheirSignalId:(NSString *)theirSignalId { - NSData *_Nullable theirIdentityKey = [[OWSIdentityManager sharedManager] identityKeyForRecipientId:theirSignalId]; + NSData *_Nullable theirIdentityKey = + [[OWSIdentityManager sharedManager] identityKeyForRecipientIdWOT:theirSignalId]; if (theirIdentityKey == nil) { OWSFail(@"%@ Missing their identity key", self.logTag); @@ -51,7 +52,7 @@ NS_ASSUME_NONNULL_BEGIN NSString *theirName = [self.contactsManager displayNameForPhoneIdentifier:theirSignalId]; NSString *mySignalId = [self.accountManager localNumber]; - NSData *myIdentityKey = [[OWSIdentityManager sharedManager] identityKeyPair].publicKey; + NSData *myIdentityKey = [[OWSIdentityManager sharedManager] identityKeyPairWithoutProtocolContext].publicKey; return [OWSFingerprint fingerprintWithMyStableId:mySignalId myIdentityKey:myIdentityKey diff --git a/SignalServiceKit/src/Storage/AxolotlStore/TSStorageManager+SessionStore.m b/SignalServiceKit/src/Storage/AxolotlStore/TSStorageManager+SessionStore.m index 1f1a272b8..180bc225c 100644 --- a/SignalServiceKit/src/Storage/AxolotlStore/TSStorageManager+SessionStore.m +++ b/SignalServiceKit/src/Storage/AxolotlStore/TSStorageManager+SessionStore.m @@ -43,7 +43,9 @@ NSString *const kSessionStoreDBConnectionKey = @"kSessionStoreDBConnectionKey"; #pragma mark - SessionStore -- (SessionRecord *)loadSession:(NSString *)contactIdentifier deviceId:(int)deviceId protocolContext:(id)protocolContext +- (SessionRecord *)loadSession:(NSString *)contactIdentifier + deviceId:(int)deviceId + protocolContext:(nullable id)protocolContext { OWSAssert(contactIdentifier.length > 0); OWSAssert(deviceId >= 0); @@ -116,7 +118,9 @@ NSString *const kSessionStoreDBConnectionKey = @"kSessionStoreDBConnectionKey"; inCollection:TSStorageManagerSessionStoreCollection]; } -- (BOOL)containsSession:(NSString *)contactIdentifier deviceId:(int)deviceId protocolContext:(id)protocolContext +- (BOOL)containsSession:(NSString *)contactIdentifier + deviceId:(int)deviceId + protocolContext:(nullable id)protocolContext { OWSAssert(contactIdentifier.length > 0); OWSAssert(deviceId >= 0); diff --git a/SignalServiceKit/src/Storage/AxolotlStore/TSStorageManager+SignedPreKeyStore.m b/SignalServiceKit/src/Storage/AxolotlStore/TSStorageManager+SignedPreKeyStore.m index bbd80e11f..5e85643fe 100644 --- a/SignalServiceKit/src/Storage/AxolotlStore/TSStorageManager+SignedPreKeyStore.m +++ b/SignalServiceKit/src/Storage/AxolotlStore/TSStorageManager+SignedPreKeyStore.m @@ -27,7 +27,7 @@ NSString *const TSStorageManagerKeyPrekeyCurrentSignedPrekeyId = @"currentSigned // Signed prekey ids must be > 0. int preKeyId = 1 + arc4random_uniform(INT32_MAX - 1); - ECKeyPair *_Nullable identityKeyPair = [[OWSIdentityManager sharedManager] identityKeyPair]; + ECKeyPair *_Nullable identityKeyPair = [[OWSIdentityManager sharedManager] identityKeyPairWithoutProtocolContext]; return [[SignedPreKeyRecord alloc] initWithId:preKeyId keyPair:keyPair