diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 009767219..dbaf598cf 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -30,10 +30,10 @@ #import #import #import +#import #import #import #import -#import #import #import #import diff --git a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m index 2da731445..a40422bc7 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m +++ b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m @@ -6,9 +6,9 @@ #import "AttachmentUploadView.h" #import "JSQMediaItem+OWS.h" #import "MIMETypeUtil.h" +#import "OWSMessageManager.h" #import "Signal-Swift.h" #import "TSAttachmentStream.h" -#import "TSMessagesManager.h" #import "UIColor+JSQMessages.h" #import "UIColor+OWS.h" #import "UIDevice+TSHardwareVersion.h" diff --git a/Signal/src/Signal-Bridging-Header.h b/Signal/src/Signal-Bridging-Header.h index 021dc8858..e658ad86f 100644 --- a/Signal/src/Signal-Bridging-Header.h +++ b/Signal/src/Signal-Bridging-Header.h @@ -72,6 +72,7 @@ #import #import #import +#import #import #import #import @@ -92,7 +93,6 @@ #import #import #import -#import #import #import #import diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 28e5b7160..0071fde56 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -76,6 +76,7 @@ #import #import #import +#import #import #import #import @@ -83,7 +84,6 @@ #import #import #import -#import #import #import @@ -288,7 +288,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { @property (nonatomic, readonly) ContactsUpdater *contactsUpdater; @property (nonatomic, readonly) OWSMessageSender *messageSender; @property (nonatomic, readonly) TSStorageManager *storageManager; -@property (nonatomic, readonly) TSMessagesManager *messagesManager; +@property (nonatomic, readonly) OWSMessageManager *messagesManager; @property (nonatomic, readonly) TSNetworkManager *networkManager; @property (nonatomic, readonly) OutboundCallInitiator *outboundCallInitiator; @property (nonatomic, readonly) OWSBlockingManager *blockingManager; @@ -371,7 +371,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { _messageSender = [Environment getCurrent].messageSender; _outboundCallInitiator = [Environment getCurrent].outboundCallInitiator; _storageManager = [TSStorageManager sharedManager]; - _messagesManager = [TSMessagesManager sharedManager]; + _messagesManager = [OWSMessageManager sharedManager]; _networkManager = [TSNetworkManager sharedManager]; _blockingManager = [OWSBlockingManager sharedManager]; _contactsViewHelper = [[ContactsViewHelper alloc] initWithDelegate:self]; diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index 9141355eb..ab71b6f42 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -17,6 +17,7 @@ #import #import #import +#import #import #import #import diff --git a/Signal/src/ViewControllers/HomeViewController.m b/Signal/src/ViewControllers/HomeViewController.m index 11752ea08..a79e7351e 100644 --- a/Signal/src/ViewControllers/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeViewController.m @@ -25,8 +25,8 @@ #import #import #import +#import #import -#import #import #import #import @@ -59,7 +59,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; @property (nonatomic, readonly) AccountManager *accountManager; @property (nonatomic, readonly) OWSContactsManager *contactsManager; @property (nonatomic, readonly) ExperienceUpgradeFinder *experienceUpgradeFinder; -@property (nonatomic, readonly) TSMessagesManager *messagesManager; +@property (nonatomic, readonly) OWSMessageManager *messagesManager; @property (nonatomic, readonly) OWSMessageSender *messageSender; @property (nonatomic, readonly) OWSBlockingManager *blockingManager; @@ -108,7 +108,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; { _accountManager = [Environment getCurrent].accountManager; _contactsManager = [Environment getCurrent].contactsManager; - _messagesManager = [TSMessagesManager sharedManager]; + _messagesManager = [OWSMessageManager sharedManager]; _messageSender = [Environment getCurrent].messageSender; _blockingManager = [OWSBlockingManager sharedManager]; _blockedPhoneNumberSet = [NSSet setWithArray:[_blockingManager blockedPhoneNumbers]]; diff --git a/Signal/src/ViewControllers/InboxTableViewCell.m b/Signal/src/ViewControllers/InboxTableViewCell.m index e4cb0b1a8..daae16a41 100644 --- a/Signal/src/ViewControllers/InboxTableViewCell.m +++ b/Signal/src/ViewControllers/InboxTableViewCell.m @@ -7,9 +7,9 @@ #import "Signal-Swift.h" #import "Util.h" #import "ViewControllerUtils.h" +#import #import #import -#import #import NS_ASSUME_NONNULL_BEGIN @@ -204,9 +204,9 @@ const NSUInteger kAvatarViewDiameter = 52; } NSAttributedString *attributedDate = [self dateAttributedString:thread.lastMessageDate]; - NSUInteger unreadCount = [[TSMessagesManager sharedManager] unreadMessagesInThread:thread]; + NSUInteger unreadCount = [[OWSMessageManager sharedManager] unreadMessagesInThread:thread]; + - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherUsersProfileDidChange:) name:kNSNotificationName_OtherUsersProfileDidChange diff --git a/Signal/src/util/ThreadUtil.m b/Signal/src/util/ThreadUtil.m index c81c8353b..fd83c8945 100644 --- a/Signal/src/util/ThreadUtil.m +++ b/Signal/src/util/ThreadUtil.m @@ -16,6 +16,8 @@ #import #import #import +#import +#import #import #import diff --git a/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m b/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m index 5be228af4..5db266a9c 100644 --- a/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m +++ b/SignalServiceKit/src/Messages/DeviceSyncing/OWSIncomingSentMessageTranscript.m @@ -3,11 +3,11 @@ // #import "OWSIncomingSentMessageTranscript.h" +#import "OWSMessageManager.h" #import "OWSSignalServiceProtos.pb.h" #import "TSContactThread.h" #import "TSGroupModel.h" #import "TSGroupThread.h" -#import "TSMessagesManager.h" #import "TSOutgoingMessage.h" #import "TSStorageManager.h" #import "TSThread.h" diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m index 825ba1aa5..6b84dda1f 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m @@ -5,9 +5,9 @@ #import "TSErrorMessage.h" #import "ContactsManagerProtocol.h" #import "NSDate+millisecondTimeStamp.h" +#import "OWSMessageManager.h" #import "TSContactThread.h" #import "TSErrorMessage_privateConstructor.h" -#import "TSMessagesManager.h" #import "TextSecureKitEnv.h" #import diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m index 67bcb20c4..b0e932e3d 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m @@ -5,11 +5,11 @@ #import "TSInvalidIdentityKeyReceivingErrorMessage.h" #import "OWSFingerprint.h" #import "OWSIdentityManager.h" +#import "OWSMessageManager.h" #import "OWSMessageReceiver.h" #import "TSContactThread.h" #import "TSDatabaseView.h" #import "TSErrorMessage_privateConstructor.h" -#import "TSMessagesManager.h" #import "TSStorageManager.h" #import #import diff --git a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m index a38c203b9..0f67fa140 100644 --- a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m +++ b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m @@ -4,9 +4,9 @@ #import "OWSBatchMessageProcessor.h" #import "NSArray+OWS.h" +#import "OWSMessageManager.h" #import "OWSSignalServiceProtos.pb.h" #import "TSDatabaseView.h" -#import "TSMessagesManager.h" #import "TSStorageManager.h" #import "TSYapDatabaseObject.h" #import "Threading.h" @@ -211,12 +211,12 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSBatchMessageProc @interface OWSMessageContentQueue : NSObject -@property (nonatomic, readonly) TSMessagesManager *messagesManager; +@property (nonatomic, readonly) OWSMessageManager *messagesManager; @property (nonatomic, readonly) YapDatabaseConnection *dbReadWriteConnection; @property (nonatomic, readonly) OWSMessageContentJobFinder *finder; @property (nonatomic) BOOL isDrainingQueue; -- (instancetype)initWithMessagesManager:(TSMessagesManager *)messagesManager +- (instancetype)initWithMessagesManager:(OWSMessageManager *)messagesManager storageManager:(TSStorageManager *)storageManager finder:(OWSMessageContentJobFinder *)finder NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; @@ -227,7 +227,7 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSBatchMessageProc @implementation OWSMessageContentQueue -- (instancetype)initWithMessagesManager:(TSMessagesManager *)messagesManager +- (instancetype)initWithMessagesManager:(OWSMessageManager *)messagesManager storageManager:(TSStorageManager *)storageManager finder:(OWSMessageContentJobFinder *)finder { @@ -356,7 +356,7 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSBatchMessageProc @implementation OWSBatchMessageProcessor - (instancetype)initWithDBConnection:(YapDatabaseConnection *)dbConnection - messagesManager:(TSMessagesManager *)messagesManager + messagesManager:(OWSMessageManager *)messagesManager storageManager:(TSStorageManager *)storageManager { OWSSingletonAssert(); @@ -380,7 +380,7 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSBatchMessageProc { // For concurrency coherency we use the same dbConnection to persist and read the unprocessed envelopes YapDatabaseConnection *dbConnection = [[TSStorageManager sharedManager].database newConnection]; - TSMessagesManager *messagesManager = [TSMessagesManager sharedManager]; + OWSMessageManager *messagesManager = [OWSMessageManager sharedManager]; TSStorageManager *storageManager = [TSStorageManager sharedManager]; return [self initWithDBConnection:dbConnection messagesManager:messagesManager storageManager:storageManager]; diff --git a/SignalServiceKit/src/Messages/OWSMessageDecrypter.h b/SignalServiceKit/src/Messages/OWSMessageDecrypter.h new file mode 100644 index 000000000..72b51deae --- /dev/null +++ b/SignalServiceKit/src/Messages/OWSMessageDecrypter.h @@ -0,0 +1,30 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "OWSMessageHandler.h" + +NS_ASSUME_NONNULL_BEGIN + +@class OWSSignalServiceProtosEnvelope; + +typedef void (^DecryptSuccessBlock)(NSData *_Nullable plaintextData); +typedef void (^DecryptFailureBlock)(); + +@interface OWSMessageDecrypter : OWSMessageHandler + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)sharedManager; + +// decryptEnvelope: can be called from any thread. +// successBlock & failureBlock may be called on any thread. +// +// Exactly one of successBlock & failureBlock will be called, +// once. +- (void)decryptEnvelope:(OWSSignalServiceProtosEnvelope *)envelope + successBlock:(DecryptSuccessBlock)successBlock + failureBlock:(DecryptFailureBlock)failureBlock; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m new file mode 100644 index 000000000..2d8f1b651 --- /dev/null +++ b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m @@ -0,0 +1,326 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "OWSMessageDecrypter.h" +#import "NSData+messagePadding.h" +#import "NotificationsProtocol.h" +#import "OWSAnalytics.h" +#import "OWSBlockingManager.h" +#import "OWSError.h" +#import "OWSIdentityManager.h" +#import "OWSSignalServiceProtos.pb.h" +#import "TSAccountManager.h" +#import "TSContactThread.h" +#import "TSErrorMessage.h" +#import "TSPreKeyManager.h" +#import "TSStorageManager+PreKeyStore.h" +#import "TSStorageManager+SessionStore.h" +#import "TSStorageManager+SignedPreKeyStore.h" +#import "TSStorageManager.h" +#import "TextSecureKitEnv.h" +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface OWSMessageDecrypter () + +@property (nonatomic, readonly) TSStorageManager *storageManager; +@property (nonatomic, readonly) YapDatabaseConnection *dbConnection; +@property (nonatomic, readonly) OWSBlockingManager *blockingManager; +@property (nonatomic, readonly) OWSIdentityManager *identityManager; + +@end + +#pragma mark - + +@implementation OWSMessageDecrypter + ++ (instancetype)sharedManager +{ + static OWSMessageDecrypter *sharedMyManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedMyManager = [[self alloc] initDefault]; + }); + return sharedMyManager; +} + +- (instancetype)initDefault +{ + TSStorageManager *storageManager = [TSStorageManager sharedManager]; + OWSIdentityManager *identityManager = [OWSIdentityManager sharedManager]; + OWSBlockingManager *blockingManager = [OWSBlockingManager sharedManager]; + + return [self initWithStorageManager:storageManager identityManager:identityManager blockingManager:blockingManager]; +} + +- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager + identityManager:(OWSIdentityManager *)identityManager + blockingManager:(OWSBlockingManager *)blockingManager +{ + self = [super init]; + + if (!self) { + return self; + } + + _storageManager = storageManager; + _identityManager = identityManager; + _blockingManager = blockingManager; + + _dbConnection = storageManager.newDatabaseConnection; + + OWSSingletonAssert(); + + return self; +} + +#pragma mark - Blocking + +- (BOOL)isEnvelopeBlocked:(OWSSignalServiceProtosEnvelope *)envelope +{ + OWSAssert(envelope); + + return [_blockingManager.blockedPhoneNumbers containsObject:envelope.source]; +} + +#pragma mark - Decryption + +- (void)decryptEnvelope:(OWSSignalServiceProtosEnvelope *)envelope + successBlock:(DecryptSuccessBlock)successBlockParameter + failureBlock:(DecryptFailureBlock)failureBlockParameter +{ + OWSAssert(envelope); + OWSAssert(successBlockParameter); + 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); + }); + }; + DecryptFailureBlock failureBlock = ^() { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + failureBlockParameter(); + }); + }; + DDLogInfo(@"%@ decrypting envelope: %@", self.tag, [self descriptionForEnvelope:envelope]); + + OWSAssert(envelope.source.length > 0); + if ([self isEnvelopeBlocked:envelope]) { + DDLogInfo(@"%@ ignoring blocked envelope: %@", self.tag, envelope.source); + failureBlock(); + return; + } + + @try { + switch (envelope.type) { + case OWSSignalServiceProtosEnvelopeTypeCiphertext: { + [self decryptSecureMessage:envelope + successBlock:^(NSData *_Nullable plaintextData) { + DDLogDebug(@"%@ decrypted secure message.", self.tag); + successBlock(plaintextData); + } + failureBlock:^(NSError *_Nullable error) { + DDLogError(@"%@ decrypting secure message from address: %@ failed with error: %@", + self.tag, + envelopeAddress(envelope), + error); + OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandleSecureMessage]); + failureBlock(); + }]; + // Return to avoid double-acknowledging. + return; + } + case OWSSignalServiceProtosEnvelopeTypePrekeyBundle: { + [self decryptPreKeyBundle:envelope + successBlock:^(NSData *_Nullable plaintextData) { + DDLogDebug(@"%@ decrypted pre-key whisper message", self.tag); + successBlock(plaintextData); + } + failureBlock:^(NSError *_Nullable error) { + DDLogError(@"%@ decrypting pre-key whisper message from address: %@ failed " + @"with error: %@", + self.tag, + envelopeAddress(envelope), + error); + OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandlePrekeyBundle]); + failureBlock(); + }]; + // Return to avoid double-acknowledging. + return; + } + // These message types don't have a payload to decrypt. + case OWSSignalServiceProtosEnvelopeTypeReceipt: + case OWSSignalServiceProtosEnvelopeTypeKeyExchange: + case OWSSignalServiceProtosEnvelopeTypeUnknown: + successBlock(nil); + // Return to avoid double-acknowledging. + return; + default: + DDLogWarn(@"Received unhandled envelope type: %d", (int)envelope.type); + break; + } + } @catch (NSException *exception) { + DDLogError(@"Received an incorrectly formatted protocol buffer: %@", exception.debugDescription); + OWSProdFail([OWSAnalyticsEvents messageManagerErrorInvalidProtocolMessage]); + } + + failureBlock(); +} + +- (void)decryptSecureMessage:(OWSSignalServiceProtosEnvelope *)envelope + successBlock:(DecryptSuccessBlock)successBlock + failureBlock:(void (^)(NSError *_Nullable error))failureBlock +{ + OWSAssert(envelope); + OWSAssert(successBlock); + OWSAssert(failureBlock); + + [self decryptEnvelope:envelope + cipherTypeName:@"Secure Message" + cipherMessageBlock:^(NSData *encryptedData) { + return [[WhisperMessage alloc] initWithData:encryptedData]; + } + successBlock:successBlock + failureBlock:failureBlock]; +} + +- (void)decryptPreKeyBundle:(OWSSignalServiceProtosEnvelope *)envelope + successBlock:(DecryptSuccessBlock)successBlock + failureBlock:(void (^)(NSError *_Nullable error))failureBlock +{ + OWSAssert(envelope); + OWSAssert(successBlock); + OWSAssert(failureBlock); + + // Check whether we need to refresh our PreKeys every time we receive a PreKeyWhisperMessage. + [TSPreKeyManager checkPreKeys]; + + [self decryptEnvelope:envelope + cipherTypeName:@"PreKey Bundle" + cipherMessageBlock:^(NSData *encryptedData) { + return [[PreKeyWhisperMessage alloc] initWithData:encryptedData]; + } + successBlock:successBlock + failureBlock:failureBlock]; +} + +- (void)decryptEnvelope:(OWSSignalServiceProtosEnvelope *)envelope + cipherTypeName:(NSString *)cipherTypeName + cipherMessageBlock:(id (^_Nonnull)(NSData *))cipherMessageBlock + successBlock:(DecryptSuccessBlock)successBlock + failureBlock:(void (^)(NSError *_Nullable error))failureBlock +{ + OWSAssert(envelope); + OWSAssert(cipherTypeName.length > 0); + OWSAssert(cipherMessageBlock); + OWSAssert(successBlock); + OWSAssert(failureBlock); + + TSStorageManager *storageManager = self.storageManager; + NSString *recipientId = envelope.source; + int deviceId = envelope.sourceDevice; + + // DEPRECATED - Remove after all clients have been upgraded. + NSData *encryptedData = envelope.hasContent ? envelope.content : envelope.legacyMessage; + if (!encryptedData) { + OWSProdFail([OWSAnalyticsEvents messageManagerErrorMessageEnvelopeHasNoContent]); + failureBlock(nil); + 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_main_queue(), ^{ + [self processException:exception envelope:envelope]; + NSString *errorDescription = [NSString + stringWithFormat:@"Exception while decrypting %@: %@", cipherTypeName, exception.description]; + NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorDescription); + failureBlock(error); + }); + } + }); +} + +- (void)processException:(NSException *)exception envelope:(OWSSignalServiceProtosEnvelope *)envelope +{ + OWSAssert([NSThread isMainThread]); + + DDLogError(@"%@ Got exception: %@ of type: %@ with reason: %@", + self.tag, + exception.description, + exception.name, + exception.reason); + + __block TSErrorMessage *errorMessage; + [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + if ([exception.name isEqualToString:NoSessionException]) { + OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorNoSession], envelope); + errorMessage = [TSErrorMessage missingSessionWithEnvelope:envelope withTransaction:transaction]; + } else if ([exception.name isEqualToString:InvalidKeyException]) { + OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorInvalidKey], envelope); + errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction]; + } else if ([exception.name isEqualToString:InvalidKeyIdException]) { + OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorInvalidKeyId], envelope); + errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction]; + } else if ([exception.name isEqualToString:DuplicateMessageException]) { + // Duplicate messages are dismissed + return; + } else if ([exception.name isEqualToString:InvalidVersionException]) { + OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorInvalidMessageVersion], envelope); + errorMessage = [TSErrorMessage invalidVersionWithEnvelope:envelope withTransaction:transaction]; + } else if ([exception.name isEqualToString:UntrustedIdentityKeyException]) { + // Should no longer get here, since we now record the new identity for incoming messages. + OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorUntrustedIdentityKeyException], envelope); + OWSFail(@"%@ Failed to trust identity on incoming message from: %@", self.tag, envelopeAddress(envelope)); + return; + } else { + OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorCorruptMessage], envelope); + errorMessage = [TSErrorMessage corruptedMessageWithEnvelope:envelope withTransaction:transaction]; + } + + [errorMessage saveWithTransaction:transaction]; + }]; + + if (errorMessage != nil) { + [self notifyForErrorMessage:errorMessage withEnvelope:envelope]; + } +} + +- (void)notifyForErrorMessage:(TSErrorMessage *)errorMessage withEnvelope:(OWSSignalServiceProtosEnvelope *)envelope +{ + TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source]; + [[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage inThread:contactThread]; +} + +#pragma mark - Logging + ++ (NSString *)tag +{ + return [NSString stringWithFormat:@"[%@]", self.class]; +} + +- (NSString *)tag +{ + return self.class.tag; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/OWSMessageHandler.h b/SignalServiceKit/src/Messages/OWSMessageHandler.h new file mode 100644 index 000000000..28534c0e3 --- /dev/null +++ b/SignalServiceKit/src/Messages/OWSMessageHandler.h @@ -0,0 +1,24 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +NS_ASSUME_NONNULL_BEGIN + +extern const NSUInteger kIncomingMessageBatchSize; + +@class OWSSignalServiceProtosContent; +@class OWSSignalServiceProtosDataMessage; +@class OWSSignalServiceProtosEnvelope; + +NSString *envelopeAddress(OWSSignalServiceProtosEnvelope *envelope); + +@interface OWSMessageHandler : NSObject + +- (NSString *)descriptionForEnvelopeType:(OWSSignalServiceProtosEnvelope *)envelope; +- (NSString *)descriptionForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope; +- (NSString *)descriptionForContent:(OWSSignalServiceProtosContent *)content; +- (NSString *)descriptionForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/OWSMessageHandler.m b/SignalServiceKit/src/Messages/OWSMessageHandler.m new file mode 100644 index 000000000..0bdf3bb16 --- /dev/null +++ b/SignalServiceKit/src/Messages/OWSMessageHandler.m @@ -0,0 +1,163 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "OWSMessageHandler.h" +#import "OWSSignalServiceProtos.pb.h" + +NS_ASSUME_NONNULL_BEGIN + +// We need to use a consistent batch size throughout +// the incoming message pipeline (i.e. in the +// "decrypt" and "process" steps), or the pipeline +// doesn't flow smoothly. +// +// We want a value that is just high enough to yield +// perf benefits. The right value is probably 5-15. +const NSUInteger kIncomingMessageBatchSize = 10; + +// used in log formatting +NSString *envelopeAddress(OWSSignalServiceProtosEnvelope *envelope) +{ + return [NSString stringWithFormat:@"%@.%d", envelope.source, (unsigned int)envelope.sourceDevice]; +} + +@implementation OWSMessageHandler + +- (NSString *)descriptionForEnvelopeType:(OWSSignalServiceProtosEnvelope *)envelope +{ + OWSAssert(envelope != nil); + + switch (envelope.type) { + case OWSSignalServiceProtosEnvelopeTypeReceipt: + return @"DeliveryReceipt"; + case OWSSignalServiceProtosEnvelopeTypeUnknown: + // Shouldn't happen + OWSProdFail([OWSAnalyticsEvents messageManagerErrorEnvelopeTypeUnknown]); + return @"Unknown"; + case OWSSignalServiceProtosEnvelopeTypeCiphertext: + return @"SignalEncryptedMessage"; + case OWSSignalServiceProtosEnvelopeTypeKeyExchange: + // Unsupported + OWSProdFail([OWSAnalyticsEvents messageManagerErrorEnvelopeTypeKeyExchange]); + return @"KeyExchange"; + case OWSSignalServiceProtosEnvelopeTypePrekeyBundle: + return @"PreKeyEncryptedMessage"; + default: + // Shouldn't happen + OWSProdFail([OWSAnalyticsEvents messageManagerErrorEnvelopeTypeOther]); + return @"Other"; + } +} + +- (NSString *)descriptionForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope +{ + OWSAssert(envelope != nil); + + return [NSString stringWithFormat:@"", + [self descriptionForEnvelopeType:envelope], + envelopeAddress(envelope), + envelope.timestamp, + (unsigned long)envelope.content.length]; +} + +/** + * We don't want to just log `content.description` because we'd potentially log message bodies for dataMesssages and + * sync transcripts + */ +- (NSString *)descriptionForContent:(OWSSignalServiceProtosContent *)content +{ + if (content.hasSyncMessage) { + return [NSString stringWithFormat:@"", [self descriptionForSyncMessage:content.syncMessage]]; + } else if (content.hasDataMessage) { + return [NSString stringWithFormat:@"", [self descriptionForDataMessage:content.dataMessage]]; + } else if (content.hasCallMessage) { + return [NSString stringWithFormat:@"", content.callMessage]; + } else if (content.hasNullMessage) { + return [NSString stringWithFormat:@"", content.nullMessage]; + } else { + // Don't fire an analytics event; if we ever add a new content type, we'd generate a ton of + // analytics traffic. + OWSFail(@"Unknown content type."); + return @"UnknownContent"; + } +} + +/** + * We don't want to just log `dataMessage.description` because we'd potentially log message contents + */ +- (NSString *)descriptionForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage +{ + NSMutableString *description = [NSMutableString new]; + + if (dataMessage.hasGroup) { + [description appendString:@"(Group:YES) "]; + } + + if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsEndSession) != 0) { + [description appendString:@"EndSession"]; + } else if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate) != 0) { + [description appendString:@"ExpirationTimerUpdate"]; + } else if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsProfileKey) != 0) { + [description appendString:@"ProfileKey"]; + } else if (dataMessage.attachments.count > 0) { + [description appendString:@"MessageWithAttachment"]; + } else { + [description appendString:@"Plain"]; + } + + return [NSString stringWithFormat:@"<%@ />", description]; +} + +/** + * We don't want to just log `syncMessage.description` because we'd potentially log message contents in sent transcripts + */ +- (NSString *)descriptionForSyncMessage:(OWSSignalServiceProtosSyncMessage *)syncMessage +{ + NSMutableString *description = [NSMutableString new]; + if (syncMessage.hasSent) { + [description appendString:@"SentTranscript"]; + } else if (syncMessage.hasRequest) { + if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeContacts) { + [description appendString:@"ContactRequest"]; + } else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeGroups) { + [description appendString:@"GroupRequest"]; + } else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeBlocked) { + [description appendString:@"BlockedRequest"]; + } else { + // Shouldn't happen + OWSFail(@"Unknown sync message request type"); + [description appendString:@"UnknownRequest"]; + } + } else if (syncMessage.hasBlocked) { + [description appendString:@"Blocked"]; + } else if (syncMessage.read.count > 0) { + [description appendString:@"ReadReceipt"]; + } else if (syncMessage.hasVerified) { + NSString *verifiedString = + [NSString stringWithFormat:@"Verification for: %@", syncMessage.verified.destination]; + [description appendString:verifiedString]; + } else { + // Shouldn't happen + OWSFail(@"Unknown sync message type"); + [description appendString:@"Unknown"]; + } + + return description; +} + +#pragma mark - Logging + ++ (NSString *)tag +{ + return [NSString stringWithFormat:@"[%@]", self.class]; +} + +- (NSString *)tag +{ + return self.class.tag; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.h b/SignalServiceKit/src/Messages/OWSMessageManager.h new file mode 100644 index 000000000..b2d260409 --- /dev/null +++ b/SignalServiceKit/src/Messages/OWSMessageManager.h @@ -0,0 +1,29 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "OWSMessageHandler.h" + +NS_ASSUME_NONNULL_BEGIN + +@class OWSSignalServiceProtosEnvelope; +@class TSThread; +@class YapDatabaseReadWriteTransaction; + +@interface OWSMessageManager : OWSMessageHandler + +- (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; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/TSMessagesManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m similarity index 71% rename from SignalServiceKit/src/Messages/TSMessagesManager.m rename to SignalServiceKit/src/Messages/OWSMessageManager.m index 869325ad7..eb1e51912 100644 --- a/SignalServiceKit/src/Messages/TSMessagesManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -2,13 +2,10 @@ // Copyright (c) 2017 Open Whisper Systems. All rights reserved. // -#import "TSMessagesManager.h" +#import "OWSMessageManager.h" #import "ContactsManagerProtocol.h" -#import "ContactsUpdater.h" #import "Cryptography.h" -#import "DataSource.h" #import "MimeTypeUtil.h" -#import "NSData+messagePadding.h" #import "NSDate+millisecondTimeStamp.h" #import "NotificationsProtocol.h" #import "OWSAttachmentsProcessor.h" @@ -17,7 +14,7 @@ #import "OWSDisappearingConfigurationUpdateInfoMessage.h" #import "OWSDisappearingMessagesConfiguration.h" #import "OWSDisappearingMessagesJob.h" -#import "OWSError.h" +#import "OWSIdentityManager.h" #import "OWSIncomingMessageFinder.h" #import "OWSIncomingSentMessageTranscript.h" #import "OWSMessageSender.h" @@ -28,33 +25,21 @@ #import "OWSSyncGroupsRequestMessage.h" #import "ProfileManagerProtocol.h" #import "TSAccountManager.h" -#import "TSAttachmentStream.h" -#import "TSCall.h" #import "TSContactThread.h" #import "TSDatabaseView.h" #import "TSGroupModel.h" #import "TSGroupThread.h" +#import "TSIncomingMessage.h" #import "TSInfoMessage.h" -#import "TSInvalidIdentityKeyReceivingErrorMessage.h" #import "TSNetworkManager.h" -#import "TSPreKeyManager.h" -#import "TSStorageHeaders.h" +#import "TSOutgoingMessage.h" +#import "TSStorageManager+SessionStore.h" +#import "TSStorageManager.h" #import "TextSecureKitEnv.h" -#import -#import NS_ASSUME_NONNULL_BEGIN -// We need to use a consistent batch size throughout -// the incoming message pipeline (i.e. in the -// "decrypt" and "process" steps), or the pipeline -// doesn't flow smoothly. -// -// We want a value that is just high enough to yield -// perf benefits. The right value is probably 5-15. -const NSUInteger kIncomingMessageBatchSize = 10; - -@interface TSMessagesManager () +@interface OWSMessageManager () @property (nonatomic, readonly) id callMessageHandler; @property (nonatomic, readonly) id contactsManager; @@ -63,15 +48,18 @@ const NSUInteger kIncomingMessageBatchSize = 10; @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 TSMessagesManager +@implementation OWSMessageManager -+ (instancetype)sharedManager { - static TSMessagesManager *sharedMyManager = nil; ++ (instancetype)sharedManager +{ + static OWSMessageManager *sharedMyManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedMyManager = [[self alloc] initDefault]; @@ -85,16 +73,14 @@ const NSUInteger kIncomingMessageBatchSize = 10; TSStorageManager *storageManager = [TSStorageManager sharedManager]; id contactsManager = [TextSecureKitEnv sharedEnv].contactsManager; id callMessageHandler = [TextSecureKitEnv sharedEnv].callMessageHandler; - ContactsUpdater *contactsUpdater = [ContactsUpdater sharedUpdater]; OWSIdentityManager *identityManager = [OWSIdentityManager sharedManager]; OWSMessageSender *messageSender = [TextSecureKitEnv sharedEnv].messageSender; - + return [self initWithNetworkManager:networkManager storageManager:storageManager callMessageHandler:callMessageHandler contactsManager:contactsManager - contactsUpdater:contactsUpdater identityManager:identityManager messageSender:messageSender]; } @@ -103,7 +89,6 @@ const NSUInteger kIncomingMessageBatchSize = 10; storageManager:(TSStorageManager *)storageManager callMessageHandler:(id)callMessageHandler contactsManager:(id)contactsManager - contactsUpdater:(ContactsUpdater *)contactsUpdater identityManager:(OWSIdentityManager *)identityManager messageSender:(OWSMessageSender *)messageSender { @@ -117,7 +102,6 @@ const NSUInteger kIncomingMessageBatchSize = 10; _networkManager = networkManager; _callMessageHandler = callMessageHandler; _contactsManager = contactsManager; - _contactsUpdater = contactsUpdater; _identityManager = identityManager; _messageSender = messageSender; @@ -145,130 +129,6 @@ const NSUInteger kIncomingMessageBatchSize = 10; [self updateApplicationBadgeCount]; } -#pragma mark - Debugging - -- (NSString *)descriptionForEnvelopeType:(OWSSignalServiceProtosEnvelope *)envelope -{ - OWSAssert(envelope != nil); - - switch (envelope.type) { - case OWSSignalServiceProtosEnvelopeTypeReceipt: - return @"DeliveryReceipt"; - case OWSSignalServiceProtosEnvelopeTypeUnknown: - // Shouldn't happen - OWSProdFail([OWSAnalyticsEvents messageManagerErrorEnvelopeTypeUnknown]); - return @"Unknown"; - case OWSSignalServiceProtosEnvelopeTypeCiphertext: - return @"SignalEncryptedMessage"; - case OWSSignalServiceProtosEnvelopeTypeKeyExchange: - // Unsupported - OWSProdFail([OWSAnalyticsEvents messageManagerErrorEnvelopeTypeKeyExchange]); - return @"KeyExchange"; - case OWSSignalServiceProtosEnvelopeTypePrekeyBundle: - return @"PreKeyEncryptedMessage"; - default: - // Shouldn't happen - OWSProdFail([OWSAnalyticsEvents messageManagerErrorEnvelopeTypeOther]); - return @"Other"; - } -} - -- (NSString *)descriptionForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope -{ - OWSAssert(envelope != nil); - - return [NSString stringWithFormat:@"", - [self descriptionForEnvelopeType:envelope], - envelopeAddress(envelope), - envelope.timestamp, - (unsigned long)envelope.content.length]; -} - -/** - * We don't want to just log `content.description` because we'd potentially log message bodies for dataMesssages and - * sync transcripts - */ -- (NSString *)descriptionForContent:(OWSSignalServiceProtosContent *)content -{ - if (content.hasSyncMessage) { - return [NSString stringWithFormat:@"", [self descriptionForSyncMessage:content.syncMessage]]; - } else if (content.hasDataMessage) { - return [NSString stringWithFormat:@"", [self descriptionForDataMessage:content.dataMessage]]; - } else if (content.hasCallMessage) { - return [NSString stringWithFormat:@"", content.callMessage]; - } else if (content.hasNullMessage) { - return [NSString stringWithFormat:@"", content.nullMessage]; - } else { - // Don't fire an analytics event; if we ever add a new content type, we'd generate a ton of - // analytics traffic. - OWSFail(@"Unknown content type."); - return @"UnknownContent"; - } -} - -/** - * We don't want to just log `dataMessage.description` because we'd potentially log message contents - */ -- (NSString *)descriptionForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage -{ - NSMutableString *description = [NSMutableString new]; - - if (dataMessage.hasGroup) { - [description appendString:@"(Group:YES) "]; - } - - if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsEndSession) != 0) { - [description appendString:@"EndSession"]; - } else if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate) != 0) { - [description appendString:@"ExpirationTimerUpdate"]; - } else if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsProfileKey) != 0) { - [description appendString:@"ProfileKey"]; - } else if (dataMessage.attachments.count > 0) { - [description appendString:@"MessageWithAttachment"]; - } else { - [description appendString:@"Plain"]; - } - - return [NSString stringWithFormat:@"<%@ />", description]; -} - -/** - * We don't want to just log `syncMessage.description` because we'd potentially log message contents in sent transcripts - */ -- (NSString *)descriptionForSyncMessage:(OWSSignalServiceProtosSyncMessage *)syncMessage -{ - NSMutableString *description = [NSMutableString new]; - if (syncMessage.hasSent) { - [description appendString:@"SentTranscript"]; - } else if (syncMessage.hasRequest) { - if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeContacts) { - [description appendString:@"ContactRequest"]; - } else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeGroups) { - [description appendString:@"GroupRequest"]; - } else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeBlocked) { - [description appendString:@"BlockedRequest"]; - } else { - // Shouldn't happen - OWSFail(@"Unknown sync message request type"); - [description appendString:@"UnknownRequest"]; - } - } else if (syncMessage.hasBlocked) { - [description appendString:@"Blocked"]; - } else if (syncMessage.read.count > 0) { - [description appendString:@"ReadReceipt"]; - } else if (syncMessage.hasVerified) { - NSString *verifiedString = - [NSString stringWithFormat:@"Verification for: %@", syncMessage.verified.destination]; - [description appendString:verifiedString]; - } else { - // Shouldn't happen - OWSFail(@"Unknown sync message type"); - [description appendString:@"Unknown"]; - } - - return description; -} - #pragma mark - Blocking - (BOOL)isEnvelopeBlocked:(OWSSignalServiceProtosEnvelope *)envelope @@ -278,178 +138,6 @@ const NSUInteger kIncomingMessageBatchSize = 10; return [_blockingManager isRecipientIdBlocked:envelope.source]; } -#pragma mark - Decryption - -- (void)decryptEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - successBlock:(DecryptSuccessBlock)successBlockParameter - failureBlock:(DecryptFailureBlock)failureBlockParameter -{ - OWSAssert(envelope); - OWSAssert(successBlockParameter); - 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); - }); - }; - DecryptFailureBlock failureBlock = ^() { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - failureBlockParameter(); - }); - }; - DDLogInfo(@"%@ decrypting envelope: %@", self.tag, [self descriptionForEnvelope:envelope]); - - OWSAssert(envelope.source.length > 0); - if ([self isEnvelopeBlocked:envelope]) { - DDLogInfo(@"%@ ignoring blocked envelope: %@", self.tag, envelope.source); - failureBlock(); - return; - } - - @try { - switch (envelope.type) { - case OWSSignalServiceProtosEnvelopeTypeCiphertext: { - [self decryptSecureMessage:envelope - successBlock:^(NSData *_Nullable plaintextData) { - DDLogDebug(@"%@ decrypted secure message.", self.tag); - successBlock(plaintextData); - } - failureBlock:^(NSError *_Nullable error) { - DDLogError(@"%@ decrypting secure message from address: %@ failed with error: %@", - self.tag, - envelopeAddress(envelope), - error); - OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandleSecureMessage]); - failureBlock(); - }]; - // Return to avoid double-acknowledging. - return; - } - case OWSSignalServiceProtosEnvelopeTypePrekeyBundle: { - [self decryptPreKeyBundle:envelope - successBlock:^(NSData *_Nullable plaintextData) { - DDLogDebug(@"%@ decrypted pre-key whisper message", self.tag); - successBlock(plaintextData); - } - failureBlock:^(NSError *_Nullable error) { - DDLogError(@"%@ decrypting pre-key whisper message from address: %@ failed " - @"with error: %@", - self.tag, - envelopeAddress(envelope), - error); - OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandlePrekeyBundle]); - failureBlock(); - }]; - // Return to avoid double-acknowledging. - return; - } - // These message types don't have a payload to decrypt. - case OWSSignalServiceProtosEnvelopeTypeReceipt: - case OWSSignalServiceProtosEnvelopeTypeKeyExchange: - case OWSSignalServiceProtosEnvelopeTypeUnknown: - successBlock(nil); - // Return to avoid double-acknowledging. - return; - default: - DDLogWarn(@"Received unhandled envelope type: %d", (int)envelope.type); - break; - } - } @catch (NSException *exception) { - DDLogError(@"Received an incorrectly formatted protocol buffer: %@", exception.debugDescription); - OWSProdFail([OWSAnalyticsEvents messageManagerErrorInvalidProtocolMessage]); - } - - failureBlock(); -} - -- (void)decryptSecureMessage:(OWSSignalServiceProtosEnvelope *)envelope - successBlock:(DecryptSuccessBlock)successBlock - failureBlock:(void (^)(NSError *_Nullable error))failureBlock -{ - OWSAssert(envelope); - OWSAssert(successBlock); - OWSAssert(failureBlock); - - [self decryptEnvelope:envelope - messageTypeName:@"Secure Message" - cipherMessageBlock:^(NSData *encryptedData) { - return [[WhisperMessage alloc] initWithData:encryptedData]; - } - successBlock:successBlock - failureBlock:failureBlock]; -} - -- (void)decryptPreKeyBundle:(OWSSignalServiceProtosEnvelope *)envelope - successBlock:(DecryptSuccessBlock)successBlock - failureBlock:(void (^)(NSError *_Nullable error))failureBlock -{ - OWSAssert(envelope); - OWSAssert(successBlock); - OWSAssert(failureBlock); - - // Check whether we need to refresh our PreKeys every time we receive a PreKeyWhisperMessage. - [TSPreKeyManager checkPreKeys]; - - [self decryptEnvelope:envelope - messageTypeName:@"PreKey Bundle" - cipherMessageBlock:^(NSData *encryptedData) { - return [[PreKeyWhisperMessage alloc] initWithData:encryptedData]; - } - successBlock:successBlock - failureBlock:failureBlock]; -} - -- (void)decryptEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - messageTypeName:(NSString *)messageTypeName - cipherMessageBlock:(id (^_Nonnull)(NSData *))cipherMessageBlock - successBlock:(DecryptSuccessBlock)successBlock - failureBlock:(void (^)(NSError *_Nullable error))failureBlock -{ - OWSAssert(envelope); - OWSAssert(messageTypeName.length > 0); - OWSAssert(cipherMessageBlock); - OWSAssert(successBlock); - OWSAssert(failureBlock); - - TSStorageManager *storageManager = self.storageManager; - NSString *recipientId = envelope.source; - int deviceId = envelope.sourceDevice; - - // DEPRECATED - Remove after all clients have been upgraded. - NSData *encryptedData = envelope.hasContent ? envelope.content : envelope.legacyMessage; - if (!encryptedData) { - OWSProdFail([OWSAnalyticsEvents messageManagerErrorMessageEnvelopeHasNoContent]); - failureBlock(nil); - 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_main_queue(), ^{ - [self processException:exception envelope:envelope]; - NSString *errorDescription = [NSString - stringWithFormat:@"Exception while decrypting %@: %@", messageTypeName, exception.description]; - NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorDescription); - failureBlock(error); - }); - } - }); -} - #pragma mark - message handling - (void)processEnvelope:(OWSSignalServiceProtosEnvelope *)envelope @@ -1205,69 +893,11 @@ const NSUInteger kIncomingMessageBatchSize = 10; return incomingMessage; } -- (void)processException:(NSException *)exception envelope:(OWSSignalServiceProtosEnvelope *)envelope -{ - OWSAssert([NSThread isMainThread]); - - DDLogError(@"%@ Got exception: %@ of type: %@ with reason: %@", - self.tag, - exception.description, - exception.name, - exception.reason); - - __block TSErrorMessage *errorMessage; - [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - if ([exception.name isEqualToString:NoSessionException]) { - OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorNoSession], envelope); - errorMessage = [TSErrorMessage missingSessionWithEnvelope:envelope withTransaction:transaction]; - } else if ([exception.name isEqualToString:InvalidKeyException]) { - OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorInvalidKey], envelope); - errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction]; - } else if ([exception.name isEqualToString:InvalidKeyIdException]) { - OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorInvalidKeyId], envelope); - errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction]; - } else if ([exception.name isEqualToString:DuplicateMessageException]) { - // Duplicate messages are dismissed - return; - } else if ([exception.name isEqualToString:InvalidVersionException]) { - OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorInvalidMessageVersion], envelope); - errorMessage = [TSErrorMessage invalidVersionWithEnvelope:envelope withTransaction:transaction]; - } else if ([exception.name isEqualToString:UntrustedIdentityKeyException]) { - // Should no longer get here, since we now record the new identity for incoming messages. - OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorUntrustedIdentityKeyException], envelope); - OWSFail(@"%@ Failed to trust identity on incoming message from: %@", self.tag, envelopeAddress(envelope)); - return; - } else { - OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorCorruptMessage], envelope); - errorMessage = [TSErrorMessage corruptedMessageWithEnvelope:envelope withTransaction:transaction]; - } - - [errorMessage saveWithTransaction:transaction]; - }]; - - if (errorMessage != nil) { - [self notifyForErrorMessage:errorMessage withEnvelope:envelope]; - } -} - -- (void)notifyForErrorMessage:(TSErrorMessage *)errorMessage withEnvelope:(OWSSignalServiceProtosEnvelope *)envelope -{ - TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source]; - [[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage inThread:contactThread]; -} - #pragma mark - helpers -// used in log formatting -NSString *envelopeAddress(OWSSignalServiceProtosEnvelope *envelope) -{ - return [NSString stringWithFormat:@"%@.%d", envelope.source, (unsigned int)envelope.sourceDevice]; -} - - (BOOL)isDataMessageGroupAvatarUpdate:(OWSSignalServiceProtosDataMessage *)dataMessage { - return dataMessage.hasGroup - && dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate + return dataMessage.hasGroup && dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate && dataMessage.group.hasAvatar; } @@ -1290,16 +920,18 @@ NSString *envelopeAddress(OWSSignalServiceProtosEnvelope *envelope) } } -- (NSUInteger)unreadMessagesCount { +- (NSUInteger)unreadMessagesCount +{ __block NSUInteger numberOfItems; [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInAllGroups]; + numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInAllGroups]; }]; return numberOfItems; } -- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread { +- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread +{ __block NSUInteger numberOfItems; [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { id databaseView = [transaction ext:TSUnreadDatabaseViewExtensionName]; @@ -1316,10 +948,11 @@ NSString *envelopeAddress(OWSSignalServiceProtosEnvelope *envelope) [[UIApplication sharedApplication] setApplicationIconBadgeNumber:numberOfItems]; } -- (NSUInteger)unreadMessagesInThread:(TSThread *)thread { +- (NSUInteger)unreadMessagesInThread:(TSThread *)thread +{ __block NSUInteger numberOfItems; [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInGroup:thread.uniqueId]; + numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInGroup:thread.uniqueId]; }]; return numberOfItems; } diff --git a/SignalServiceKit/src/Messages/OWSMessageReceiver.m b/SignalServiceKit/src/Messages/OWSMessageReceiver.m index 46d655ff4..5e7e7feb3 100644 --- a/SignalServiceKit/src/Messages/OWSMessageReceiver.m +++ b/SignalServiceKit/src/Messages/OWSMessageReceiver.m @@ -5,9 +5,9 @@ #import "OWSMessageReceiver.h" #import "NSArray+OWS.h" #import "OWSBatchMessageProcessor.h" +#import "OWSMessageDecrypter.h" #import "OWSSignalServiceProtos.pb.h" #import "TSDatabaseView.h" -#import "TSMessagesManager.h" #import "TSStorageManager.h" #import "TSYapDatabaseObject.h" #import "Threading.h" @@ -208,14 +208,14 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin @interface OWSMessageDecryptQueue : NSObject -@property (nonatomic, readonly) TSMessagesManager *messagesManager; +@property (nonatomic, readonly) OWSMessageDecrypter *messageDecrypter; @property (nonatomic, readonly) OWSBatchMessageProcessor *batchMessageProcessor; @property (nonatomic, readonly) OWSMessageDecryptJobFinder *finder; @property (nonatomic) BOOL isDrainingQueue; -- (instancetype)initWithMessagesManager:(TSMessagesManager *)messagesManager - batchMessageProcessor:(OWSBatchMessageProcessor *)batchMessageProcessor - finder:(OWSMessageDecryptJobFinder *)finder NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithMessageDecrypter:(OWSMessageDecrypter *)messageDecrypter + batchMessageProcessor:(OWSBatchMessageProcessor *)batchMessageProcessor + finder:(OWSMessageDecryptJobFinder *)finder NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; @end @@ -224,9 +224,9 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin @implementation OWSMessageDecryptQueue -- (instancetype)initWithMessagesManager:(TSMessagesManager *)messagesManager - batchMessageProcessor:(OWSBatchMessageProcessor *)batchMessageProcessor - finder:(OWSMessageDecryptJobFinder *)finder +- (instancetype)initWithMessageDecrypter:(OWSMessageDecrypter *)messageDecrypter + batchMessageProcessor:(OWSBatchMessageProcessor *)batchMessageProcessor + finder:(OWSMessageDecryptJobFinder *)finder { OWSSingletonAssert(); @@ -235,7 +235,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin return self; } - _messagesManager = messagesManager; + _messageDecrypter = messageDecrypter; _batchMessageProcessor = batchMessageProcessor; _finder = finder; _isDrainingQueue = NO; @@ -337,7 +337,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin OWSAssert(unprocessedJobs.count > 0); OWSMessageDecryptJob *job = unprocessedJobs.firstObject; [unprocessedJobs removeObjectAtIndex:0]; - [self.messagesManager decryptEnvelope:job.envelopeProto + [self.messageDecrypter decryptEnvelope:job.envelopeProto successBlock:^(NSData *_Nullable plaintextData) { if (plaintextData) { plaintextDataMap[job.uniqueId] = plaintextData; @@ -384,7 +384,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin @implementation OWSMessageReceiver - (instancetype)initWithDBConnection:(YapDatabaseConnection *)dbConnection - messagesManager:(TSMessagesManager *)messagesManager + messageDecrypter:(OWSMessageDecrypter *)messageDecrypter batchMessageProcessor:(OWSBatchMessageProcessor *)batchMessageProcessor { OWSSingletonAssert(); @@ -396,9 +396,9 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin OWSMessageDecryptJobFinder *finder = [[OWSMessageDecryptJobFinder alloc] initWithDBConnection:dbConnection]; OWSMessageDecryptQueue *processingQueue = - [[OWSMessageDecryptQueue alloc] initWithMessagesManager:messagesManager - batchMessageProcessor:batchMessageProcessor - finder:finder]; + [[OWSMessageDecryptQueue alloc] initWithMessageDecrypter:messageDecrypter + batchMessageProcessor:batchMessageProcessor + finder:finder]; _processingQueue = processingQueue; @@ -409,11 +409,11 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin { // For concurrency coherency we use the same dbConnection to persist and read the unprocessed envelopes YapDatabaseConnection *dbConnection = [[TSStorageManager sharedManager].database newConnection]; - TSMessagesManager *messagesManager = [TSMessagesManager sharedManager]; + OWSMessageDecrypter *messageDecrypter = [OWSMessageDecrypter sharedManager]; OWSBatchMessageProcessor *batchMessageProcessor = [OWSBatchMessageProcessor sharedInstance]; return [self initWithDBConnection:dbConnection - messagesManager:messagesManager + messageDecrypter:messageDecrypter batchMessageProcessor:batchMessageProcessor]; } diff --git a/SignalServiceKit/src/Messages/TSMessagesManager.h b/SignalServiceKit/src/Messages/TSMessagesManager.h deleted file mode 100644 index b3766722a..000000000 --- a/SignalServiceKit/src/Messages/TSMessagesManager.h +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. -// - -#import "TSIncomingMessage.h" -#import "TSInvalidIdentityKeySendingErrorMessage.h" -#import "TSOutgoingMessage.h" - -NS_ASSUME_NONNULL_BEGIN - -extern const NSUInteger kIncomingMessageBatchSize; - -@class TSNetworkManager; -@class TSStorageManager; -@class OWSSignalServiceProtosEnvelope; -@class OWSSignalServiceProtosDataMessage; -@class ContactsUpdater; -@class OWSMessageSender; -@protocol ContactsManagerProtocol; -@protocol OWSCallMessageHandler; - -typedef void (^DecryptSuccessBlock)(NSData *_Nullable plaintextData); -typedef void (^DecryptFailureBlock)(); -typedef void (^MessageManagerCompletionBlock)(); - -@interface TSMessagesManager : NSObject - -- (instancetype)init NS_UNAVAILABLE; -+ (instancetype)sharedManager; - -@property (nonatomic, readonly) YapDatabaseConnection *dbConnection; -@property (nonatomic, readonly) TSNetworkManager *networkManager; -@property (nonatomic, readonly) ContactsUpdater *contactsUpdater; - -// decryptEnvelope: can be called from any thread. -// successBlock & failureBlock may be called on any thread. -// -// Exactly one of successBlock & failureBlock will be called, -// once. -- (void)decryptEnvelope:(OWSSignalServiceProtosEnvelope *)envelope - successBlock:(DecryptSuccessBlock)successBlock - failureBlock:(DecryptFailureBlock)failureBlock; - -// 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; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m b/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m index 7359c6bd0..154df77c9 100644 --- a/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m +++ b/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m @@ -5,13 +5,14 @@ #import "TSSocketManager.h" #import "Cryptography.h" #import "NSTimer+OWS.h" +#import "OWSMessageManager.h" #import "OWSMessageReceiver.h" #import "OWSSignalService.h" +#import "OWSSignalServiceProtos.pb.h" #import "OWSWebsocketSecurityPolicy.h" #import "SubProtocol.pb.h" #import "TSAccountManager.h" #import "TSConstants.h" -#import "TSMessagesManager.h" #import "TSStorageManager+keyingMaterial.h" #import "Threading.h" diff --git a/SignalServiceKit/src/Util/OWSAnalytics.h b/SignalServiceKit/src/Util/OWSAnalytics.h index 6494dae61..840f6a8cd 100755 --- a/SignalServiceKit/src/Util/OWSAnalytics.h +++ b/SignalServiceKit/src/Util/OWSAnalytics.h @@ -134,8 +134,8 @@ typedef NSDictionary *_Nonnull (^OWSProdAssertParametersBlock)() #define OWSProdCritical(__eventName) OWSProdEventWParams(OWSAnalyticsSeverityCritical, __eventName, nil) -#pragma mark - TSMessagesManager macros -// Defined here rather than in TSMessagesManager so that our analytic event extraction script +#pragma mark - OWSMessageManager macros +// Defined here rather than in OWSMessageManager so that our analytic event extraction script // can properly detect the event names. // // The debug logs can be more verbose than the analytics events.