2017-09-14 17:00:30 +02:00
|
|
|
//
|
2018-01-30 21:05:04 +01:00
|
|
|
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
2017-09-14 17:00:30 +02:00
|
|
|
//
|
|
|
|
|
|
|
|
#import "OWSMessageDecrypter.h"
|
|
|
|
#import "NSData+messagePadding.h"
|
|
|
|
#import "NotificationsProtocol.h"
|
|
|
|
#import "OWSAnalytics.h"
|
|
|
|
#import "OWSBlockingManager.h"
|
|
|
|
#import "OWSError.h"
|
|
|
|
#import "OWSIdentityManager.h"
|
2018-03-05 15:30:58 +01:00
|
|
|
#import "OWSPrimaryStorage+PreKeyStore.h"
|
|
|
|
#import "OWSPrimaryStorage+SessionStore.h"
|
|
|
|
#import "OWSPrimaryStorage+SignedPreKeyStore.h"
|
|
|
|
#import "OWSPrimaryStorage.h"
|
2017-09-14 17:00:30 +02:00
|
|
|
#import "OWSSignalServiceProtos.pb.h"
|
|
|
|
#import "TSAccountManager.h"
|
|
|
|
#import "TSContactThread.h"
|
|
|
|
#import "TSErrorMessage.h"
|
|
|
|
#import "TSPreKeyManager.h"
|
|
|
|
#import "TextSecureKitEnv.h"
|
|
|
|
#import <AxolotlKit/AxolotlExceptions.h>
|
|
|
|
#import <AxolotlKit/SessionCipher.h>
|
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
|
|
|
|
@interface OWSMessageDecrypter ()
|
|
|
|
|
2018-03-05 15:30:58 +01:00
|
|
|
@property (nonatomic, readonly) OWSPrimaryStorage *primaryStorage;
|
2017-09-14 17:00:30 +02:00
|
|
|
@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
|
|
|
|
{
|
2018-03-05 15:30:58 +01:00
|
|
|
OWSPrimaryStorage *primaryStorage = [OWSPrimaryStorage sharedManager];
|
2017-09-14 17:00:30 +02:00
|
|
|
OWSIdentityManager *identityManager = [OWSIdentityManager sharedManager];
|
|
|
|
OWSBlockingManager *blockingManager = [OWSBlockingManager sharedManager];
|
|
|
|
|
2018-03-05 15:30:58 +01:00
|
|
|
return [self initWithPrimaryStorage:primaryStorage identityManager:identityManager blockingManager:blockingManager];
|
2017-09-14 17:00:30 +02:00
|
|
|
}
|
|
|
|
|
2018-03-05 15:30:58 +01:00
|
|
|
- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage
|
2017-09-14 17:00:30 +02:00
|
|
|
identityManager:(OWSIdentityManager *)identityManager
|
|
|
|
blockingManager:(OWSBlockingManager *)blockingManager
|
|
|
|
{
|
|
|
|
self = [super init];
|
|
|
|
|
|
|
|
if (!self) {
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2018-03-05 15:30:58 +01:00
|
|
|
_primaryStorage = primaryStorage;
|
2017-09-14 17:00:30 +02:00
|
|
|
_identityManager = identityManager;
|
|
|
|
_blockingManager = blockingManager;
|
|
|
|
|
2018-03-05 15:30:58 +01:00
|
|
|
_dbConnection = primaryStorage.newDatabaseConnection;
|
2017-09-14 17:00:30 +02:00
|
|
|
|
|
|
|
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
|
2018-01-30 21:05:04 +01:00
|
|
|
successBlock:(DecryptSuccessBlock)successBlock
|
2017-09-14 17:00:30 +02:00
|
|
|
failureBlock:(DecryptFailureBlock)failureBlockParameter
|
|
|
|
{
|
|
|
|
OWSAssert(envelope);
|
2018-01-30 21:05:04 +01:00
|
|
|
OWSAssert(successBlock);
|
2017-09-14 17:00:30 +02:00
|
|
|
OWSAssert(failureBlockParameter);
|
|
|
|
OWSAssert([TSAccountManager isRegistered]);
|
|
|
|
|
2018-01-30 21:05:04 +01:00
|
|
|
// successBlock is called synchronously so that we can avail ourselves of
|
|
|
|
// the transaction.
|
|
|
|
//
|
|
|
|
// Ensure that failureBlock is called on a worker queue.
|
2017-09-14 17:00:30 +02:00
|
|
|
DecryptFailureBlock failureBlock = ^() {
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
failureBlockParameter();
|
|
|
|
});
|
|
|
|
};
|
2017-11-08 20:04:51 +01:00
|
|
|
DDLogInfo(@"%@ decrypting envelope: %@", self.logTag, [self descriptionForEnvelope:envelope]);
|
2017-09-14 17:00:30 +02:00
|
|
|
|
|
|
|
OWSAssert(envelope.source.length > 0);
|
|
|
|
if ([self isEnvelopeBlocked:envelope]) {
|
2017-11-08 20:04:51 +01:00
|
|
|
DDLogInfo(@"%@ ignoring blocked envelope: %@", self.logTag, envelope.source);
|
2017-09-14 17:00:30 +02:00
|
|
|
failureBlock();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
@try {
|
|
|
|
switch (envelope.type) {
|
|
|
|
case OWSSignalServiceProtosEnvelopeTypeCiphertext: {
|
|
|
|
[self decryptSecureMessage:envelope
|
2018-01-30 21:05:04 +01:00
|
|
|
successBlock:^(NSData *_Nullable plaintextData, YapDatabaseReadWriteTransaction *transaction) {
|
2017-11-08 20:04:51 +01:00
|
|
|
DDLogDebug(@"%@ decrypted secure message.", self.logTag);
|
2018-01-30 21:05:04 +01:00
|
|
|
successBlock(plaintextData, transaction);
|
2017-09-14 17:00:30 +02:00
|
|
|
}
|
|
|
|
failureBlock:^(NSError *_Nullable error) {
|
|
|
|
DDLogError(@"%@ decrypting secure message from address: %@ failed with error: %@",
|
2017-11-08 20:04:51 +01:00
|
|
|
self.logTag,
|
2017-09-14 17:00:30 +02:00
|
|
|
envelopeAddress(envelope),
|
|
|
|
error);
|
|
|
|
OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandleSecureMessage]);
|
|
|
|
failureBlock();
|
|
|
|
}];
|
|
|
|
// Return to avoid double-acknowledging.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case OWSSignalServiceProtosEnvelopeTypePrekeyBundle: {
|
|
|
|
[self decryptPreKeyBundle:envelope
|
2018-01-30 21:05:04 +01:00
|
|
|
successBlock:^(NSData *_Nullable plaintextData, YapDatabaseReadWriteTransaction *transaction) {
|
2017-11-08 20:04:51 +01:00
|
|
|
DDLogDebug(@"%@ decrypted pre-key whisper message", self.logTag);
|
2018-01-30 21:05:04 +01:00
|
|
|
successBlock(plaintextData, transaction);
|
2017-09-14 17:00:30 +02:00
|
|
|
}
|
|
|
|
failureBlock:^(NSError *_Nullable error) {
|
|
|
|
DDLogError(@"%@ decrypting pre-key whisper message from address: %@ failed "
|
|
|
|
@"with error: %@",
|
2017-11-08 20:04:51 +01:00
|
|
|
self.logTag,
|
2017-09-14 17:00:30 +02:00
|
|
|
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:
|
2018-01-30 21:05:04 +01:00
|
|
|
case OWSSignalServiceProtosEnvelopeTypeUnknown: {
|
2018-02-02 18:32:23 +01:00
|
|
|
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
|
|
|
successBlock(nil, transaction);
|
|
|
|
}];
|
2017-09-14 17:00:30 +02:00
|
|
|
// Return to avoid double-acknowledging.
|
|
|
|
return;
|
2018-01-30 21:05:04 +01:00
|
|
|
}
|
2017-09-14 17:00:30 +02:00
|
|
|
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<CipherMessage> (^_Nonnull)(NSData *))cipherMessageBlock
|
|
|
|
successBlock:(DecryptSuccessBlock)successBlock
|
|
|
|
failureBlock:(void (^)(NSError *_Nullable error))failureBlock
|
|
|
|
{
|
|
|
|
OWSAssert(envelope);
|
|
|
|
OWSAssert(cipherTypeName.length > 0);
|
|
|
|
OWSAssert(cipherMessageBlock);
|
|
|
|
OWSAssert(successBlock);
|
|
|
|
OWSAssert(failureBlock);
|
|
|
|
|
2018-03-05 15:30:58 +01:00
|
|
|
OWSPrimaryStorage *primaryStorage = self.primaryStorage;
|
2017-09-14 17:00:30 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-02-02 18:32:23 +01:00
|
|
|
[self.dbConnection
|
2018-01-31 17:44:51 +01:00
|
|
|
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
|
2018-01-30 21:05:04 +01:00
|
|
|
@try {
|
|
|
|
id<CipherMessage> cipherMessage = cipherMessageBlock(encryptedData);
|
2018-03-05 15:30:58 +01:00
|
|
|
SessionCipher *cipher = [[SessionCipher alloc] initWithSessionStore:primaryStorage
|
|
|
|
preKeyStore:primaryStorage
|
|
|
|
signedPreKeyStore:primaryStorage
|
2018-01-30 21:05:04 +01:00
|
|
|
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);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}];
|
2017-09-14 17:00:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)processException:(NSException *)exception envelope:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
{
|
|
|
|
DDLogError(@"%@ Got exception: %@ of type: %@ with reason: %@",
|
2017-11-08 20:04:51 +01:00
|
|
|
self.logTag,
|
2017-09-14 17:00:30 +02:00
|
|
|
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);
|
2017-11-08 20:04:51 +01:00
|
|
|
OWSFail(
|
|
|
|
@"%@ Failed to trust identity on incoming message from: %@", self.logTag, envelopeAddress(envelope));
|
2017-09-14 17:00:30 +02:00
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorCorruptMessage], envelope);
|
|
|
|
errorMessage = [TSErrorMessage corruptedMessageWithEnvelope:envelope withTransaction:transaction];
|
|
|
|
}
|
|
|
|
|
2017-09-21 17:55:25 +02:00
|
|
|
OWSAssert(errorMessage);
|
2017-09-14 17:00:30 +02:00
|
|
|
[errorMessage saveWithTransaction:transaction];
|
|
|
|
}];
|
|
|
|
|
|
|
|
if (errorMessage != nil) {
|
|
|
|
[self notifyForErrorMessage:errorMessage withEnvelope:envelope];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)notifyForErrorMessage:(TSErrorMessage *)errorMessage withEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
{
|
|
|
|
TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source];
|
2017-09-28 16:01:17 +02:00
|
|
|
[[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage inThread:contactThread];
|
2017-09-14 17:00:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_END
|