2014-11-19 21:17:53 +01:00
|
|
|
//
|
|
|
|
// TSMessagesHandler.m
|
|
|
|
// TextSecureKit
|
|
|
|
//
|
|
|
|
// Created by Frederic Jacobs on 11/11/14.
|
|
|
|
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "TSMessagesManager.h"
|
|
|
|
|
|
|
|
#import <AxolotlKit/SessionCipher.h>
|
|
|
|
|
|
|
|
#import "Cryptography.h"
|
|
|
|
#import "IncomingPushMessageSignal.pb.h"
|
|
|
|
#import "NSData+Base64.h"
|
|
|
|
|
|
|
|
#import "TSIncomingMessage.h"
|
|
|
|
#import "TSErrorMessage.h"
|
|
|
|
#import "TSInfoMessage.h"
|
|
|
|
|
|
|
|
#import "TSStorageManager+keyingMaterial.h"
|
|
|
|
#import "TSStorageManager+IdentityKeyStore.h"
|
|
|
|
#import "TSStorageManager+SessionStore.h"
|
|
|
|
#import "TSStorageManager+SignedPreKeyStore.h"
|
|
|
|
#import "TSStorageManager+PreKeyStore.h"
|
|
|
|
#import "TSNetworkManager.h"
|
|
|
|
#import "TSSubmitMessageRequest.h"
|
|
|
|
|
|
|
|
#import "NSData+messagePadding.h"
|
|
|
|
|
|
|
|
#import <CocoaLumberjack/DDLog.h>
|
|
|
|
|
|
|
|
#define ddLogLevel LOG_LEVEL_DEBUG
|
|
|
|
|
|
|
|
@implementation TSMessagesManager
|
|
|
|
|
|
|
|
+ (instancetype)sharedManager {
|
|
|
|
static TSMessagesManager *sharedMyManager = nil;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
sharedMyManager = [[self alloc] init];
|
|
|
|
});
|
|
|
|
return sharedMyManager;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (instancetype)init{
|
|
|
|
self = [super init];
|
|
|
|
|
|
|
|
if (self) {
|
2014-11-21 14:38:37 +01:00
|
|
|
_dbConnection = [TSStorageManager sharedManager].newDatabaseConnection;
|
2014-11-19 21:17:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2014-11-21 19:52:34 +01:00
|
|
|
- (void)handleMessageSignal:(NSData*)signalData{
|
2014-11-21 14:38:37 +01:00
|
|
|
NSString *base64String = [[NSString alloc] initWithData:signalData encoding:NSUTF8StringEncoding];
|
|
|
|
|
|
|
|
NSData *encryptedSignal = [NSData dataFromBase64String:base64String];
|
|
|
|
NSData *decryptedPayload = [Cryptography decryptAppleMessagePayload:encryptedSignal
|
|
|
|
withSignalingKey:TSStorageManager.signalingKey];
|
2014-11-19 21:17:53 +01:00
|
|
|
|
|
|
|
if (!decryptedPayload) {
|
2014-11-21 14:38:37 +01:00
|
|
|
DDLogWarn(@"Failed to decrypt incoming payload or bad HMAC");
|
2014-11-19 21:17:53 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-21 14:38:37 +01:00
|
|
|
@try {
|
|
|
|
IncomingPushMessageSignal *messageSignal = [IncomingPushMessageSignal parseFromData:decryptedPayload];
|
|
|
|
|
|
|
|
switch (messageSignal.type) {
|
|
|
|
case IncomingPushMessageSignalTypeCiphertext:
|
|
|
|
[self handleSecureMessage:messageSignal];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IncomingPushMessageSignalTypePrekeyBundle:
|
|
|
|
[self handlePreKeyBundle:messageSignal];
|
|
|
|
break;
|
|
|
|
|
2014-11-19 21:17:53 +01:00
|
|
|
// Other messages are just dismissed for now.
|
2014-11-21 14:38:37 +01:00
|
|
|
|
|
|
|
case IncomingPushMessageSignalTypeKeyExchange:
|
|
|
|
DDLogWarn(@"Received Key Exchange Message, not supported");
|
|
|
|
break;
|
|
|
|
case IncomingPushMessageSignalTypePlaintext:
|
|
|
|
DDLogWarn(@"Received a plaintext message");
|
|
|
|
break;
|
|
|
|
case IncomingPushMessageSignalTypeReceipt:
|
|
|
|
DDLogInfo(@"Received a delivery receipt");
|
|
|
|
break;
|
|
|
|
case IncomingPushMessageSignalTypeUnknown:
|
|
|
|
DDLogWarn(@"Received an unknown message type");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@catch (NSException *exception) {
|
|
|
|
DDLogWarn(@"Received an incorrectly formatted protocol buffer: %@", exception.debugDescription);
|
2014-11-19 21:17:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)handleSecureMessage:(IncomingPushMessageSignal*)secureMessage{
|
|
|
|
@synchronized(self){
|
|
|
|
TSStorageManager *storageManager = [TSStorageManager sharedManager];
|
|
|
|
NSString *recipientId = secureMessage.source;
|
|
|
|
int deviceId = secureMessage.sourceDevice;
|
|
|
|
|
|
|
|
if (![storageManager containsSession:recipientId deviceId:deviceId]) {
|
|
|
|
// Deal with failure
|
|
|
|
}
|
|
|
|
|
2014-11-21 14:38:37 +01:00
|
|
|
PushMessageContent *content;
|
2014-11-19 21:17:53 +01:00
|
|
|
|
|
|
|
@try {
|
2014-11-21 14:38:37 +01:00
|
|
|
|
|
|
|
WhisperMessage *message = [[WhisperMessage alloc] initWithData:secureMessage.message];
|
|
|
|
|
2014-11-19 21:17:53 +01:00
|
|
|
SessionCipher *cipher = [[SessionCipher alloc] initWithSessionStore:storageManager
|
|
|
|
preKeyStore:storageManager
|
|
|
|
signedPreKeyStore:storageManager
|
|
|
|
identityKeyStore:storageManager
|
|
|
|
recipientId:recipientId
|
|
|
|
deviceId:deviceId];
|
|
|
|
|
2014-11-21 14:38:37 +01:00
|
|
|
NSData *plaintext = [[cipher decrypt:message] removePadding];
|
|
|
|
|
|
|
|
content = [PushMessageContent parseFromData:plaintext];
|
2014-11-19 21:17:53 +01:00
|
|
|
}
|
|
|
|
@catch (NSException *exception) {
|
|
|
|
[self processException:exception pushSignal:secureMessage];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
[self handleIncomingMessage:secureMessage withPushContent:content];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)handlePreKeyBundle:(IncomingPushMessageSignal*)preKeyMessage{
|
|
|
|
@synchronized(self){
|
|
|
|
TSStorageManager *storageManager = [TSStorageManager sharedManager];
|
|
|
|
NSString *recipientId = preKeyMessage.source;
|
|
|
|
int deviceId = preKeyMessage.sourceDevice;
|
|
|
|
|
2014-11-21 14:38:37 +01:00
|
|
|
PushMessageContent *content;
|
2014-11-19 21:17:53 +01:00
|
|
|
|
|
|
|
@try {
|
2014-11-21 14:38:37 +01:00
|
|
|
PreKeyWhisperMessage *message = [[PreKeyWhisperMessage alloc] initWithData:preKeyMessage.message];
|
|
|
|
|
2014-11-19 21:17:53 +01:00
|
|
|
SessionCipher *cipher = [[SessionCipher alloc] initWithSessionStore:storageManager
|
|
|
|
preKeyStore:storageManager
|
|
|
|
signedPreKeyStore:storageManager
|
|
|
|
identityKeyStore:storageManager
|
|
|
|
recipientId:recipientId
|
|
|
|
deviceId:deviceId];
|
|
|
|
|
2014-11-21 14:38:37 +01:00
|
|
|
NSData *plaintext = [[cipher decrypt:message] removePadding];
|
|
|
|
|
|
|
|
content = [PushMessageContent parseFromData:plaintext];
|
2014-11-19 21:17:53 +01:00
|
|
|
}
|
|
|
|
@catch (NSException *exception) {
|
|
|
|
[self processException:exception pushSignal:preKeyMessage];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
[self handleIncomingMessage:preKeyMessage withPushContent:content];
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)handleIncomingMessage:(IncomingPushMessageSignal*)incomingMessage withPushContent:(PushMessageContent*)content{
|
|
|
|
if ((content.flags & PushMessageContentFlagsEndSession) != 0) {
|
|
|
|
DDLogVerbose(@"Received end session message...");
|
|
|
|
[self handleEndSessionMessage:incomingMessage withContent:content];
|
|
|
|
} else if (content.hasGroup && (content.group.type != PushMessageContentGroupContextTypeDeliver)) {
|
|
|
|
DDLogVerbose(@"Received push group update message...");
|
|
|
|
[self handleGroupMessage:incomingMessage withContent:content];
|
|
|
|
} else if (content.attachments.count > 0) {
|
|
|
|
DDLogVerbose(@"Received push media message (attachement) ...");
|
|
|
|
[self handleReceivedMediaMessage:incomingMessage withContent:content];
|
|
|
|
} else {
|
|
|
|
DDLogVerbose(@"Received push text message...");
|
|
|
|
[self handleReceivedTextMessage:incomingMessage withContent:content];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)handleEndSessionMessage:(IncomingPushMessageSignal*)message withContent:(PushMessageContent*)content{
|
2014-11-21 14:38:37 +01:00
|
|
|
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
|
|
|
TSContactThread *thread = [TSContactThread threadWithContactId:message.source transaction:transaction];
|
|
|
|
uint64_t timeStamp = message.timestamp;
|
|
|
|
|
|
|
|
if (thread){
|
|
|
|
[[[TSInfoMessage alloc] initWithTimestamp:timeStamp inThread:thread messageType:TSInfoMessageTypeSessionDidEnd] saveWithTransaction:transaction];
|
|
|
|
}
|
|
|
|
}];
|
2014-11-19 21:17:53 +01:00
|
|
|
|
|
|
|
[[TSStorageManager sharedManager] deleteAllSessionsForContact:message.source];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)handleGroupMessage:(IncomingPushMessageSignal*)message withContent:(PushMessageContent*)content{
|
|
|
|
// TO DO
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)handleReceivedMediaMessage:(IncomingPushMessageSignal*)message withContent:(PushMessageContent*)content{
|
|
|
|
// TO DO
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)handleReceivedTextMessage:(IncomingPushMessageSignal*)message withContent:(PushMessageContent*)content{
|
|
|
|
uint64_t timeStamp = message.timestamp;
|
|
|
|
NSString *body = content.body;
|
|
|
|
NSData *groupId = content.hasGroup?content.group.id:nil;
|
|
|
|
|
2014-11-21 14:38:37 +01:00
|
|
|
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
|
|
|
TSIncomingMessage *incomingMessage;
|
|
|
|
TSThread *thread;
|
|
|
|
if (groupId) {
|
|
|
|
TSGroupThread *gThread = [TSGroupThread threadWithGroupId:groupId];
|
|
|
|
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timeStamp inThread:gThread authorId:message.source messageBody:body attachements:nil];
|
|
|
|
thread = gThread;
|
|
|
|
} else{
|
|
|
|
TSContactThread *cThread = [TSContactThread threadWithContactId:message.source transaction:transaction];
|
|
|
|
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timeStamp inThread:cThread messageBody:body attachements:nil];
|
|
|
|
thread = cThread;
|
|
|
|
}
|
|
|
|
|
|
|
|
[incomingMessage saveWithTransaction:transaction];
|
|
|
|
[thread saveWithTransaction:transaction];
|
|
|
|
}];
|
2014-11-19 21:17:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)processException:(NSException*)exception pushSignal:(IncomingPushMessageSignal*)signal{
|
|
|
|
NSLog(@"Got exception: %@", exception.description);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)processException:(NSException*)exception outgoingMessage:(TSOutgoingMessage*)message{
|
|
|
|
NSLog(@"Got exception: %@", exception.description);
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|