pull/105/head
Niels Andriesse 5 years ago
parent 21c92eadfc
commit 85083d1969

@ -27,9 +27,9 @@ NSString *const kNSNotificationKey_ContactPubKey = @"kNSNotificationKey_ContactP
// Our state before we decrypt the message // Our state before we decrypt the message
SessionState *_Nullable state = [self getCurrentState:protocolContext]; SessionState *_Nullable state = [self getCurrentState:protocolContext];
// Loki: Verify incoming friend request messages // Verify incoming friend request messages
if (!state) { if (!state) {
[self throws_verifyFriendRequestAcceptPreKeyForMessage:whisperMessage protocolContext:protocolContext]; [self throws_validatePreKeysForFriendRequestAcceptance:whisperMessage protocolContext:protocolContext];
} }
// While decrypting our state may change internally // While decrypting our state may change internally
@ -132,16 +132,16 @@ NSString *const kNSNotificationKey_ContactPubKey = @"kNSNotificationKey_ContactP
[self.sessionStore storeSession:self.recipientId deviceId:self.deviceId session:record protocolContext:protocolContext]; [self.sessionStore storeSession:self.recipientId deviceId:self.deviceId session:record protocolContext:protocolContext];
} }
/// Check that we have matching prekeys in the case of a `PreKeyWhisperMessage` /// Check that we have matching pre keys in the case of a `PreKeyWhisperMessage`.
/// This is so that we don't trigger a false friend request accept on unknown contacts /// This is so that we don't trigger a false friend request accept on unknown contacts.
- (void)throws_verifyFriendRequestAcceptPreKeyForMessage:(id<CipherMessage>)whisperMessage protocolContext:(nullable id)protocolContext { - (void)throws_validatePreKeysForFriendRequestAcceptance:(id<CipherMessage>)whisperMessage protocolContext:(nullable id)protocolContext {
OWSAssertDebug([protocolContext isKindOfClass:[YapDatabaseReadTransaction class]]); OWSAssertDebug([protocolContext isKindOfClass:[YapDatabaseReadTransaction class]]);
YapDatabaseReadTransaction *transaction = protocolContext; YapDatabaseReadTransaction *transaction = protocolContext;
// We only want to look at `PreKeyWhisperMessage` // Ignore anything that isn't a `PreKeyWhisperMessage`
if (![whisperMessage isKindOfClass:[PreKeyWhisperMessage class]]) { return; } if (![whisperMessage isKindOfClass:[PreKeyWhisperMessage class]]) { return; }
// We need the primary storage to access contact prekeys // Check the pre key store
if (![self.prekeyStore isKindOfClass:[OWSPrimaryStorage class]]) { return; } if (![self.prekeyStore isKindOfClass:[OWSPrimaryStorage class]]) { return; }
PreKeyWhisperMessage *preKeyMessage = whisperMessage; PreKeyWhisperMessage *preKeyMessage = whisperMessage;
@ -149,11 +149,11 @@ NSString *const kNSNotificationKey_ContactPubKey = @"kNSNotificationKey_ContactP
PreKeyRecord *_Nullable storedPreKey = [primaryStorage getPreKeyForContact:self.recipientId transaction:transaction]; PreKeyRecord *_Nullable storedPreKey = [primaryStorage getPreKeyForContact:self.recipientId transaction:transaction];
if (!storedPreKey) { if (!storedPreKey) {
OWSRaiseException(@"LokiInvalidPreKey", @"Received a friend request from a public key for which no prekey bundle was created."); OWSRaiseException(@"Loki", @"Received a friend request from a public key for which no pre key bundle was created.");
} }
if (storedPreKey.Id != preKeyMessage.prekeyID) { if (storedPreKey.Id != preKeyMessage.prekeyID) {
OWSRaiseException(@"LokiPreKeyIdsDontMatch", @"Received a PreKeyWhisperMessage (friend request accept) from an unknown source."); OWSRaiseException(@"Loki", @"Received a PreKeyWhisperMessage (friend request accept) from an unknown source.");
} }
} }

@ -365,20 +365,12 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo
{ {
OWSAssertDebug(AppReadiness.isAppReady); OWSAssertDebug(AppReadiness.isAppReady);
// Don't process incoming messages in app extensions. if (!CurrentAppContext().isMainApp) { return; }
if (!CurrentAppContext().isMainApp) { if (!self.tsAccountManager.isRegisteredAndReady) { return; }
return;
}
if (!self.tsAccountManager.isRegisteredAndReady) {
return;
}
dispatch_async(self.serialQueue, ^{ dispatch_async(self.serialQueue, ^{
if (self.isDrainingQueue) { if (self.isDrainingQueue) { return; }
return;
}
self.isDrainingQueue = YES; self.isDrainingQueue = YES;
[self drainQueueWorkStep]; [self drainQueueWorkStep];
}); });
} }
@ -387,7 +379,7 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo
{ {
AssertOnDispatchQueue(self.serialQueue); AssertOnDispatchQueue(self.serialQueue);
// We want a value that is just high enough to yield perf benefits. // We want a value that is just high enough to yield performance benefits
const NSUInteger kIncomingMessageBatchSize = 32; const NSUInteger kIncomingMessageBatchSize = 32;
NSArray<OWSMessageContentJob *> *batchJobs = [self.finder nextJobsForBatchSize:kIncomingMessageBatchSize]; NSArray<OWSMessageContentJob *> *batchJobs = [self.finder nextJobsForBatchSize:kIncomingMessageBatchSize];
@ -525,7 +517,7 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo
transaction:(YapDatabaseReadWriteTransaction *)transaction transaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
if (envelopeData.length < 1) { if (envelopeData.length < 1) {
OWSFailDebug(@"Empty envelope."); OWSFailDebug(@"Received an empty envelope.");
return; return;
} }
OWSAssert(transaction); OWSAssert(transaction);

@ -177,7 +177,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) { OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
// Ensure all blocked messages are discarded. // Ensure all blocked messages are discarded.
if ([self isEnvelopeSenderBlocked:envelope]) { if ([self isEnvelopeSenderBlocked:envelope]) {
OWSLogInfo(@"Ignoring blocked envelope: %@", envelope.source); OWSLogInfo(@"Ignoring blocked envelope from: %@.", envelope.source);
return failureBlock(); return failureBlock();
} }
@ -195,21 +195,21 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
}; };
@try { @try {
OWSLogInfo(@"decrypting envelope: %@", [self descriptionForEnvelope:envelope]); OWSLogInfo(@"Decrypting envelope: %@.", [self descriptionForEnvelope:envelope]);
if (envelope.type != SSKProtoEnvelopeTypeUnidentifiedSender) { if (envelope.type != SSKProtoEnvelopeTypeUnidentifiedSender) {
if (!envelope.hasSource || envelope.source.length < 1 || ![ECKeyPair isValidHexEncodedPublicKeyWithCandidate:envelope.source]) { if (!envelope.hasSource || envelope.source.length < 1 || ![ECKeyPair isValidHexEncodedPublicKeyWithCandidate:envelope.source]) {
OWSFailDebug(@"incoming envelope has invalid source"); OWSFailDebug(@"Incoming envelope with invalid source.");
return failureBlock(); return failureBlock();
} }
if (!envelope.hasSourceDevice || envelope.sourceDevice < 1) { if (!envelope.hasSourceDevice || envelope.sourceDevice < 1) {
OWSFailDebug(@"incoming envelope has invalid source device"); OWSFailDebug(@"Incoming envelope with invalid source device.");
return failureBlock(); return failureBlock();
} }
// We block UD messages later, after they are decrypted. // We block UD messages later, after they are decrypted.
if ([self isEnvelopeSenderBlocked:envelope]) { if ([self isEnvelopeSenderBlocked:envelope]) {
OWSLogInfo(@"ignoring blocked envelope: %@", envelope.source); OWSLogInfo(@"Ignoring blocked envelope from: %@.", envelope.source);
return failureBlock(); return failureBlock();
} }
} }
@ -224,7 +224,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
successBlock(result, transaction); successBlock(result, transaction);
} }
failureBlock:^(NSError * _Nullable error) { failureBlock:^(NSError * _Nullable error) {
OWSLogError(@"Decrypting friend request message from address: %@ failed with error: %@.", OWSLogError(@"Decrypting friend request message from: %@ failed with error: %@.",
envelopeAddress(envelope), envelopeAddress(envelope),
error); error);
failureBlock(); failureBlock();
@ -237,11 +237,11 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
[self throws_decryptSecureMessage:envelope [self throws_decryptSecureMessage:envelope
envelopeData:envelopeData envelopeData:envelopeData
successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) { successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
OWSLogDebug(@"decrypted secure message."); OWSLogDebug(@"Decrypted secure message.");
successBlock(result, transaction); successBlock(result, transaction);
} }
failureBlock:^(NSError *_Nullable error) { failureBlock:^(NSError *_Nullable error) {
OWSLogError(@"decrypting secure message from address: %@ failed with error: %@", OWSLogError(@"Decrypting secure message from: %@ failed with error: %@.",
envelopeAddress(envelope), envelopeAddress(envelope),
error); error);
OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandleSecureMessage]); OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandleSecureMessage]);
@ -254,12 +254,11 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
[self throws_decryptPreKeyBundle:envelope [self throws_decryptPreKeyBundle:envelope
envelopeData:envelopeData envelopeData:envelopeData
successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) { successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
OWSLogDebug(@"decrypted pre-key whisper message"); OWSLogDebug(@"Decrypted pre key bundle message.");
successBlock(result, transaction); successBlock(result, transaction);
} }
failureBlock:^(NSError *_Nullable error) { failureBlock:^(NSError *_Nullable error) {
OWSLogError(@"decrypting pre-key whisper message from address: %@ failed " OWSLogError(@"Decrypting pre key bundle message from: %@ failed with error: %@.",
@"with error: %@",
envelopeAddress(envelope), envelopeAddress(envelope),
error); error);
OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandlePrekeyBundle]); OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandlePrekeyBundle]);
@ -287,12 +286,11 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
case SSKProtoEnvelopeTypeUnidentifiedSender: { case SSKProtoEnvelopeTypeUnidentifiedSender: {
[self decryptUnidentifiedSender:envelope [self decryptUnidentifiedSender:envelope
successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) { successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
OWSLogDebug(@"decrypted unidentified sender message"); OWSLogDebug(@"Decrypted unidentified sender message.");
successBlock(result, transaction); successBlock(result, transaction);
} }
failureBlock:^(NSError *_Nullable error) { failureBlock:^(NSError *_Nullable error) {
OWSLogError(@"decrypting unidentified sender message from address: %@ failed " OWSLogError(@"Decrypting unidentified sender message from: %@ failed with error: %@.",
@"with error: %@",
envelopeAddress(envelope), envelopeAddress(envelope),
error); error);
OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandleUnidentifiedSenderMessage]); OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandleUnidentifiedSenderMessage]);
@ -302,11 +300,11 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
return; return;
} }
default: default:
OWSLogWarn(@"Received unhandled envelope type: %d", (int)envelope.type); OWSLogWarn(@"Received unhandled envelope type: %d.", (int)envelope.type);
break; break;
} }
} @catch (NSException *exception) { } @catch (NSException *exception) {
OWSFailDebug(@"Received an invalid envelope: %@", exception.debugDescription); OWSFailDebug(@"Received an invalid envelope: %@.", exception.debugDescription);
OWSProdFail([OWSAnalyticsEvents messageManagerErrorInvalidProtocolMessage]); OWSProdFail([OWSAnalyticsEvents messageManagerErrorInvalidProtocolMessage]);
[[self.primaryStorage newDatabaseConnection] [[self.primaryStorage newDatabaseConnection]
@ -342,7 +340,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
NSData *_Nullable plaintextData = [[cipher decryptWithMessage:encryptedData] removePadding]; NSData *_Nullable plaintextData = [[cipher decryptWithMessage:encryptedData] removePadding];
if (!plaintextData) { if (!plaintextData) {
NSString *errorString = [NSString stringWithFormat:@"Failed to decrypt friend request message for: %@.", recipientId]; NSString *errorString = [NSString stringWithFormat:@"Failed to decrypt friend request message from: %@.", recipientId];
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorString); NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorString);
return failureBlock(error); return failureBlock(error);
} }
@ -419,7 +417,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
NSData *encryptedData = envelope.content ?: envelope.legacyMessage; NSData *encryptedData = envelope.content ?: envelope.legacyMessage;
if (!encryptedData) { if (!encryptedData) {
OWSProdFail([OWSAnalyticsEvents messageManagerErrorMessageEnvelopeHasNoContent]); OWSProdFail([OWSAnalyticsEvents messageManagerErrorMessageEnvelopeHasNoContent]);
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, @"Envelope has no content"); NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, @"Envelope has no content.");
return failureBlock(error); return failureBlock(error);
} }
@ -435,15 +433,8 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
deviceId:deviceId]; deviceId:deviceId];
// plaintextData may be nil for some envelope types. // plaintextData may be nil for some envelope types.
NSData *_Nullable plaintextData = NSData *_Nullable plaintextData = [[cipher throws_lokiDecrypt:cipherMessage protocolContext:transaction] removePadding];
[[cipher throws_lokiDecrypt:cipherMessage protocolContext:transaction] removePadding];
/* Loki: Original code
* ================
NSData *_Nullable plaintextData =
[[cipher throws_decrypt:cipherMessage protocolContext:transaction] removePadding];
* ================
*/
OWSMessageDecryptResult *result = [OWSMessageDecryptResult resultWithEnvelopeData:envelopeData OWSMessageDecryptResult *result = [OWSMessageDecryptResult resultWithEnvelopeData:envelopeData
plaintextData:plaintextData plaintextData:plaintextData
source:envelope.source source:envelope.source
@ -454,7 +445,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self processException:exception envelope:envelope]; [self processException:exception envelope:envelope];
NSString *errorDescription = [NSString NSString *errorDescription = [NSString
stringWithFormat:@"Exception while decrypting %@: %@", cipherTypeName, exception.description]; stringWithFormat:@"Exception while decrypting %@: %@.", cipherTypeName, exception.description];
OWSLogError(@"%@", errorDescription); OWSLogError(@"%@", errorDescription);
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorDescription); NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorDescription);
failureBlock(error); failureBlock(error);
@ -497,8 +488,8 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
identityStore:self.identityManager identityStore:self.identityManager
error:&cipherError]; error:&cipherError];
if (cipherError || !cipher) { if (cipherError || !cipher) {
OWSFailDebug(@"Could not create secret session cipher: %@", cipherError); OWSFailDebug(@"Could not create secret session cipher: %@.", cipherError);
cipherError = EnsureDecryptError(cipherError, @"Could not create secret session cipher"); cipherError = EnsureDecryptError(cipherError, @"Could not create secret session cipher.");
return failureBlock(cipherError); return failureBlock(cipherError);
} }
@ -514,9 +505,9 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
if (!decryptResult) { if (!decryptResult) {
if (!decryptError) { if (!decryptError) {
OWSFailDebug(@"Caller should provide specific error"); OWSFailDebug(@"Caller should provide specific error.");
NSError *error = OWSErrorWithCodeDescription( NSError *error = OWSErrorWithCodeDescription(
OWSErrorCodeFailedToDecryptUDMessage, @"Could not decrypt UD message"); OWSErrorCodeFailedToDecryptUDMessage, @"Could not decrypt UD message.");
return failureBlock(error); return failureBlock(error);
} }
@ -565,7 +556,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self processException:underlyingException envelope:identifiedEnvelope]; [self processException:underlyingException envelope:identifiedEnvelope];
NSString *errorDescription = [NSString NSString *errorDescription = [NSString
stringWithFormat:@"Exception while decrypting ud message: %@", underlyingException.description]; stringWithFormat:@"Exception while decrypting UD message: %@.", underlyingException.description];
OWSLogError(@"%@", errorDescription); OWSLogError(@"%@", errorDescription);
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorDescription); NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorDescription);
failureBlock(error); failureBlock(error);
@ -580,7 +571,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
return; return;
} }
OWSFailDebug(@"Could not decrypt UD message: %@", underlyingError); OWSFailDebug(@"Could not decrypt UD message: %@.", underlyingError);
failureBlock(underlyingError); failureBlock(underlyingError);
return; return;
} }

@ -260,26 +260,27 @@ NS_ASSUME_NONNULL_BEGIN
return; return;
} }
if (!CurrentAppContext().isMainApp) { if (!CurrentAppContext().isMainApp) {
OWSFail(@"Not main app."); OWSFail(@"Not the main app.");
return; return;
} }
OWSLogInfo(@"handling decrypted envelope: %@", [self descriptionForEnvelope:envelope]); OWSLogInfo(@"Handling decrypted envelope: %@.", [self descriptionForEnvelope:envelope]);
if (!wasReceivedByUD) { if (!wasReceivedByUD) {
if (!envelope.hasSource || envelope.source.length < 1) { if (!envelope.hasSource || envelope.source.length < 1) {
OWSFailDebug(@"incoming envelope has invalid source"); OWSFailDebug(@"Incoming envelope with invalid source.");
return; return;
} }
if (!envelope.hasSourceDevice || envelope.sourceDevice < 1) { if (!envelope.hasSourceDevice || envelope.sourceDevice < 1) {
OWSFailDebug(@"incoming envelope has invalid source device"); OWSFailDebug(@"Incoming envelope with invalid source device.");
return; return;
} }
} }
OWSAssertDebug(![self isEnvelopeSenderBlocked:envelope]); OWSAssertDebug(![self isEnvelopeSenderBlocked:envelope]);
// Loki: Ignore any friend requests that we got before restoration // Loki: Ignore any friend requests from before restoration
// The envelope type is set during UD decryption.
uint64_t restorationTime = [NSNumber numberWithDouble:[OWSPrimaryStorage.sharedManager getRestorationTime]].unsignedLongLongValue; uint64_t restorationTime = [NSNumber numberWithDouble:[OWSPrimaryStorage.sharedManager getRestorationTime]].unsignedLongLongValue;
if (envelope.type == SSKProtoEnvelopeTypeFriendRequest && envelope.timestamp < restorationTime * 1000) { if (envelope.type == SSKProtoEnvelopeTypeFriendRequest && envelope.timestamp < restorationTime * 1000) {
[LKLogger print:@"[Loki] Ignoring friend request received before restoration."]; [LKLogger print:@"[Loki] Ignoring friend request received before restoration."];
@ -417,7 +418,7 @@ NS_ASSUME_NONNULL_BEGIN
return; return;
} }
if (envelope.sourceDevice < 1) { if (envelope.sourceDevice < 1) {
OWSFailDebug(@"Invaid source device."); OWSFailDebug(@"Invalid source device.");
return; return;
} }
@ -426,23 +427,24 @@ NS_ASSUME_NONNULL_BEGIN
sourceDeviceId:envelope.sourceDevice sourceDeviceId:envelope.sourceDevice
transaction:transaction]; transaction:transaction];
if (duplicateEnvelope) { if (duplicateEnvelope) {
OWSLogInfo(@"Ignoring previously received envelope from %@ with timestamp: %llu", OWSLogInfo(@"Ignoring previously received envelope from: %@ with timestamp: %llu.",
envelopeAddress(envelope), envelopeAddress(envelope),
envelope.timestamp); envelope.timestamp);
return; return;
} }
// Loki: Handle friend request acceptance if needed // Loki: Handle friend request acceptance if needed
// The envelope type is set during UD decryption.
[self handleFriendRequestAcceptanceIfNeededWithEnvelope:envelope transaction:transaction]; [self handleFriendRequestAcceptanceIfNeededWithEnvelope:envelope transaction:transaction];
if (envelope.content != nil) { if (envelope.content != nil) {
NSError *error; NSError *error;
SSKProtoContent *_Nullable contentProto = [SSKProtoContent parseData:plaintextData error:&error]; SSKProtoContent *_Nullable contentProto = [SSKProtoContent parseData:plaintextData error:&error];
if (error || !contentProto) { if (error || !contentProto) {
OWSFailDebug(@"could not parse proto: %@", error); OWSFailDebug(@"Could not parse proto due to error: %@.", error);
return; return;
} }
OWSLogInfo(@"handling content: <Content: %@>", [self descriptionForContent:contentProto]); OWSLogInfo(@"Handling content: <Content: %@>.", [self descriptionForContent:contentProto]);
// Loki: Workaround for duplicate sync transcript issue // Loki: Workaround for duplicate sync transcript issue
if (contentProto.syncMessage != nil && contentProto.syncMessage.sent != nil) { if (contentProto.syncMessage != nil && contentProto.syncMessage.sent != nil) {
@ -460,7 +462,8 @@ NS_ASSUME_NONNULL_BEGIN
} }
[self.primaryStorage setPreKeyBundle:bundle forContact:envelope.source transaction:transaction]; [self.primaryStorage setPreKeyBundle:bundle forContact:envelope.source transaction:transaction];
// Loki: If we received a friend request, but we were already friends with this user, then reset the session // Loki: If we received a friend request, but we were already friends with this user, reset the session
// The envelope type is set during UD decryption.
if (envelope.type == SSKProtoEnvelopeTypeFriendRequest) { if (envelope.type == SSKProtoEnvelopeTypeFriendRequest) {
TSContactThread *thread = [TSContactThread getThreadWithContactId:envelope.source transaction:transaction]; TSContactThread *thread = [TSContactThread getThreadWithContactId:envelope.source transaction:transaction];
if (thread && thread.isContactFriend) { if (thread && thread.isContactFriend) {
@ -558,13 +561,13 @@ NS_ASSUME_NONNULL_BEGIN
return; return;
} }
// Loki: Don't process session request messages // Loki: Don't process session request messages any further
if ((dataMessage.flags & SSKProtoDataMessageFlagsSessionRequest) != 0) { return; } if ((dataMessage.flags & SSKProtoDataMessageFlagsSessionRequest) != 0) { return; }
// Loki: Don't process session restore messages // Loki: Don't process session restore messages any further
if ((dataMessage.flags & SSKProtoDataMessageFlagsSessionRestore) != 0) { return; } if ((dataMessage.flags & SSKProtoDataMessageFlagsSessionRestore) != 0) { return; }
if ([self isDataMessageBlocked:dataMessage envelope:envelope]) { if ([self isDataMessageBlocked:dataMessage envelope:envelope]) {
NSString *logMessage = [NSString stringWithFormat:@"Ignoring blocked message from sender: %@", envelope.source]; NSString *logMessage = [NSString stringWithFormat:@"Ignoring blocked message from sender: %@.", envelope.source];
if (dataMessage.group) { if (dataMessage.group) {
logMessage = [logMessage stringByAppendingFormat:@" in group: %@", dataMessage.group.id]; logMessage = [logMessage stringByAppendingFormat:@" in group: %@", dataMessage.group.id];
} }
@ -574,14 +577,12 @@ NS_ASSUME_NONNULL_BEGIN
if (dataMessage.hasTimestamp) { if (dataMessage.hasTimestamp) {
if (dataMessage.timestamp <= 0) { if (dataMessage.timestamp <= 0) {
OWSFailDebug(@"Ignoring message with invalid data message timestamp: %@", envelope.source); OWSFailDebug(@"Ignoring data message with invalid timestamp: %@.", envelope.source);
// TODO: Add analytics.
return; return;
} }
// This prevents replay attacks by the service. // This prevents replay attacks by the service.
if (dataMessage.timestamp != envelope.timestamp) { if (dataMessage.timestamp != envelope.timestamp) {
OWSFailDebug(@"Ignoring message with non-matching data message timestamp: %@", envelope.source); OWSFailDebug(@"Ignoring data message with non-matching timestamp: %@.", envelope.source);
// TODO: Add analytics.
return; return;
} }
} }
@ -607,7 +608,7 @@ NS_ASSUME_NONNULL_BEGIN
[self sendGroupInfoRequest:dataMessage.group.id envelope:envelope transaction:transaction]; [self sendGroupInfoRequest:dataMessage.group.id envelope:envelope transaction:transaction];
return; return;
} else { } else {
OWSLogInfo(@"Ignoring group message for unknown group from: %@", envelope.source); OWSLogInfo(@"Ignoring group message for unknown group from: %@.", envelope.source);
return; return;
} }
} }
@ -890,7 +891,7 @@ NS_ASSUME_NONNULL_BEGIN
TSThread *_Nullable thread = [self threadForEnvelope:envelope dataMessage:dataMessage transaction:transaction]; TSThread *_Nullable thread = [self threadForEnvelope:envelope dataMessage:dataMessage transaction:transaction];
if (!thread) { if (!thread) {
OWSFailDebug(@"ignoring media message for unknown group."); OWSFailDebug(@"Ignoring media message for unknown group.");
return; return;
} }
@ -905,17 +906,17 @@ NS_ASSUME_NONNULL_BEGIN
[message saveWithTransaction:transaction]; [message saveWithTransaction:transaction];
OWSLogDebug(@"incoming attachment message: %@", message.debugDescription); OWSLogDebug(@"Incoming attachment message: %@.", message.debugDescription);
[self.attachmentDownloads downloadAttachmentsForMessage:message [self.attachmentDownloads downloadAttachmentsForMessage:message
transaction:transaction transaction:transaction
success:^(NSArray<TSAttachmentStream *> *attachmentStreams) { success:^(NSArray<TSAttachmentStream *> *attachmentStreams) {
OWSLogDebug(@"successfully fetched attachments: %lu for message: %@", OWSLogDebug(@"Successfully fetched attachments: %lu for message: %@.",
(unsigned long)attachmentStreams.count, (unsigned long)attachmentStreams.count,
message); message);
} }
failure:^(NSError *error) { failure:^(NSError *error) {
OWSLogError(@"failed to fetch attachments for message: %@ with error: %@", message, error); OWSLogError(@"Failed to fetch attachments for message: %@ with error: %@.", message, error);
}]; }];
} }
@ -1402,7 +1403,9 @@ NS_ASSUME_NONNULL_BEGIN
dispatch_queue_t messageProcessingQueue = SSKEnvironment.shared.batchMessageProcessor.processingQueue.serialQueue; dispatch_queue_t messageProcessingQueue = SSKEnvironment.shared.batchMessageProcessor.processingQueue.serialQueue;
AssertOnDispatchQueue(messageProcessingQueue); AssertOnDispatchQueue(messageProcessingQueue);
// The envelope source is set during UD decryption.
if ([ECKeyPair isValidHexEncodedPublicKeyWithCandidate:envelope.source]) { if ([ECKeyPair isValidHexEncodedPublicKeyWithCandidate:envelope.source]) {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[[LKAPI getDestinationsFor:envelope.source inTransaction:transaction].ensureOn(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() { [[LKAPI getDestinationsFor:envelope.source inTransaction:transaction].ensureOn(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() {
@ -1432,7 +1435,7 @@ NS_ASSUME_NONNULL_BEGIN
// We distinguish between the old group state (if any) and the new group state. // We distinguish between the old group state (if any) and the new group state.
TSGroupThread *_Nullable oldGroupThread = [TSGroupThread threadWithGroupId:groupId transaction:transaction]; TSGroupThread *_Nullable oldGroupThread = [TSGroupThread threadWithGroupId:groupId transaction:transaction];
if (oldGroupThread) { if (oldGroupThread) {
// Loki: Try to figure out removed members // Loki: Determine removed members
removedMemberIds = [NSMutableSet setWithArray:oldGroupThread.groupModel.groupMemberIds]; removedMemberIds = [NSMutableSet setWithArray:oldGroupThread.groupModel.groupMemberIds];
[removedMemberIds minusSet:newMemberIds]; [removedMemberIds minusSet:newMemberIds];
[removedMemberIds removeObject:hexEncodedPublicKey]; [removedMemberIds removeObject:hexEncodedPublicKey];
@ -1562,7 +1565,7 @@ NS_ASSUME_NONNULL_BEGIN
// Loki: Don't process friend requests in group chats // Loki: Don't process friend requests in group chats
if (body.length == 0 && attachmentPointers.count < 1 && !contact) { if (body.length == 0 && attachmentPointers.count < 1 && !contact) {
OWSLogWarn(@"ignoring empty incoming message from: %@ for group: %@ with timestamp: %lu", OWSLogWarn(@"Ignoring empty incoming message from: %@ for group: %@ with timestamp: %lu.",
hexEncodedPublicKey, hexEncodedPublicKey,
groupId, groupId,
(unsigned long)timestamp); (unsigned long)timestamp);
@ -1591,14 +1594,14 @@ NS_ASSUME_NONNULL_BEGIN
return incomingMessage; return incomingMessage;
} }
default: { default: {
OWSLogWarn(@"Ignoring unknown group message type: %d", (int)dataMessage.group.type); OWSLogWarn(@"Ignoring unknown group message type: %d.", (int)dataMessage.group.type);
return nil; return nil;
} }
} }
} else { } else {
// Loki: A message from a secondary device should appear as if it came from the primary device; the underlying // Loki: A message from a slave device should appear as if it came from the master device; the underlying
// friend request logic, however, should still be specific to the secondary device. // friend request logic, however, should still be specific to the slave device.
// Loki: Get the master hex encoded public key and thread // Loki: Get the master hex encoded public key and thread
NSString *hexEncodedPublicKey = envelope.source; NSString *hexEncodedPublicKey = envelope.source;
@ -1606,7 +1609,7 @@ NS_ASSUME_NONNULL_BEGIN
TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:hexEncodedPublicKey transaction:transaction]; TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:hexEncodedPublicKey transaction:transaction];
TSContactThread *masterThread = [TSContactThread getOrCreateThreadWithContactId:masterHexEncodedPublicKey transaction:transaction]; TSContactThread *masterThread = [TSContactThread getOrCreateThreadWithContactId:masterHexEncodedPublicKey transaction:transaction];
OWSLogDebug(@"incoming message from: %@ with timestamp: %lu", hexEncodedPublicKey, (unsigned long)timestamp); OWSLogDebug(@"Incoming message from: %@ with timestamp: %lu.", hexEncodedPublicKey, (unsigned long)timestamp);
[[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithDisappearingDuration:dataMessage.expireTimer [[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithDisappearingDuration:dataMessage.expireTimer
thread:masterThread thread:masterThread
@ -1665,7 +1668,7 @@ NS_ASSUME_NONNULL_BEGIN
[self handleFriendRequestMessageIfNeededWithEnvelope:envelope data:dataMessage message:incomingMessage thread:thread transaction:transaction]; [self handleFriendRequestMessageIfNeededWithEnvelope:envelope data:dataMessage message:incomingMessage thread:thread transaction:transaction];
if (body.length == 0 && attachmentPointers.count < 1 && !contact) { if (body.length == 0 && attachmentPointers.count < 1 && !contact) {
OWSLogWarn(@"ignoring empty incoming message from: %@ with timestamp: %lu", OWSLogWarn(@"Ignoring empty incoming message from: %@ with timestamp: %lu.",
hexEncodedPublicKey, hexEncodedPublicKey,
(unsigned long)timestamp); (unsigned long)timestamp);
return nil; return nil;
@ -1703,13 +1706,11 @@ NS_ASSUME_NONNULL_BEGIN
if (profileKey.length == kAES256_KeyByteLength) { if (profileKey.length == kAES256_KeyByteLength) {
[self.profileManager setProfileKeyData:profileKey forRecipientId:recipientId avatarURL:url]; [self.profileManager setProfileKeyData:profileKey forRecipientId:recipientId avatarURL:url];
} else { } else {
OWSFailDebug( OWSFailDebug(@"Unexpected profile key length:%lu on message from:%@", (unsigned long)profileKey.length, recipientId);
@"Unexpected profile key length:%lu on message from:%@", (unsigned long)profileKey.length, recipientId);
} }
} }
} }
// Loki: Establish a session if there is no session between the memebers of a group
- (void)establishSessionsWithMembersIfNeeded:(NSArray *)members forThread:(TSGroupThread *)thread transaction:(YapDatabaseReadWriteTransaction *)transaction - (void)establishSessionsWithMembersIfNeeded:(NSArray *)members forThread:(TSGroupThread *)thread transaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
NSString *userHexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey; NSString *userHexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey;
@ -1759,6 +1760,7 @@ NS_ASSUME_NONNULL_BEGIN
if (envelope.isGroupChatMessage) { if (envelope.isGroupChatMessage) {
return NSLog(@"[Loki] Ignoring friend request in group chat.", @""); return NSLog(@"[Loki] Ignoring friend request in group chat.", @"");
} }
// The envelope type is set during UD decryption.
if (envelope.type != SSKProtoEnvelopeTypeFriendRequest) { if (envelope.type != SSKProtoEnvelopeTypeFriendRequest) {
return NSLog(@"[Loki] Ignoring friend request logic for non friend request type envelope."); return NSLog(@"[Loki] Ignoring friend request logic for non friend request type envelope.");
} }
@ -1804,6 +1806,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)handleFriendRequestAcceptanceIfNeededWithEnvelope:(SSKProtoEnvelope *)envelope transaction:(YapDatabaseReadWriteTransaction *)transaction { - (void)handleFriendRequestAcceptanceIfNeededWithEnvelope:(SSKProtoEnvelope *)envelope transaction:(YapDatabaseReadWriteTransaction *)transaction {
// If we get an envelope that isn't a friend request, then we can infer that we had to use // If we get an envelope that isn't a friend request, then we can infer that we had to use
// Signal cipher decryption and thus that we have a session with the other person. // Signal cipher decryption and thus that we have a session with the other person.
// The envelope type is set during UD decryption.
if (envelope.isGroupChatMessage || envelope.type == SSKProtoEnvelopeTypeFriendRequest) return; if (envelope.isGroupChatMessage || envelope.type == SSKProtoEnvelopeTypeFriendRequest) return;
// Currently this uses `envelope.source` but with sync messages we'll need to use the message sender ID // Currently this uses `envelope.source` but with sync messages we'll need to use the message sender ID
TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction]; TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction];
@ -1896,7 +1899,7 @@ NS_ASSUME_NONNULL_BEGIN
}]; }];
} }
failure:^(NSError *error) { failure:^(NSError *error) {
OWSLogWarn(@"failed to download attachment for message: %lu with error: %@", OWSLogWarn(@"Failed to download attachment for message: %lu with error: %@.",
(unsigned long)incomingMessage.timestamp, (unsigned long)incomingMessage.timestamp,
error); error);
}]; }];
@ -2017,7 +2020,7 @@ NS_ASSUME_NONNULL_BEGIN
} }
} }
# pragma mark - Loki Session # pragma mark - Loki Session Handling
- (void)handleNewSessionAdopted:(NSNotification *)notification { - (void)handleNewSessionAdopted:(NSNotification *)notification {
NSString *hexEncodedPublicKey = notification.userInfo[kNSNotificationKey_ContactPubKey]; NSString *hexEncodedPublicKey = notification.userInfo[kNSNotificationKey_ContactPubKey];

@ -323,20 +323,12 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
{ {
OWSAssertDebug(AppReadiness.isAppReady); OWSAssertDebug(AppReadiness.isAppReady);
// Don't decrypt messages in app extensions. if (!CurrentAppContext().isMainApp) { return; }
if (!CurrentAppContext().isMainApp) { if (!self.tsAccountManager.isRegisteredAndReady) { return; }
return;
}
if (!self.tsAccountManager.isRegisteredAndReady) {
return;
}
dispatch_async(self.serialQueue, ^{ dispatch_async(self.serialQueue, ^{
if (self.isDrainingQueue) { if (self.isDrainingQueue) { return; }
return;
}
self.isDrainingQueue = YES; self.isDrainingQueue = YES;
[self drainQueueWorkStep]; [self drainQueueWorkStep];
}); });
} }
@ -346,6 +338,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
AssertOnDispatchQueue(self.serialQueue); AssertOnDispatchQueue(self.serialQueue);
OWSMessageDecryptJob *_Nullable job = [self.finder nextJob]; OWSMessageDecryptJob *_Nullable job = [self.finder nextJob];
if (!job) { if (!job) {
self.isDrainingQueue = NO; self.isDrainingQueue = NO;
OWSLogVerbose(@"Queue is drained."); OWSLogVerbose(@"Queue is drained.");
@ -369,8 +362,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
- (BOOL)wasReceivedByUD:(SSKProtoEnvelope *)envelope - (BOOL)wasReceivedByUD:(SSKProtoEnvelope *)envelope
{ {
return ( return (envelope.type == SSKProtoEnvelopeTypeUnidentifiedSender && (!envelope.hasSource || envelope.source.length < 1));
envelope.type == SSKProtoEnvelopeTypeUnidentifiedSender && (!envelope.hasSource || envelope.source.length < 1));
} }
- (void)processJob:(OWSMessageDecryptJob *)job completion:(void (^)(BOOL))completion - (void)processJob:(OWSMessageDecryptJob *)job completion:(void (^)(BOOL))completion
@ -379,9 +371,9 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
OWSAssertDebug(job); OWSAssertDebug(job);
SSKProtoEnvelope *_Nullable envelope = job.envelopeProto; SSKProtoEnvelope *_Nullable envelope = job.envelopeProto;
if (!envelope) { if (!envelope) {
OWSFailDebug(@"Could not parse proto."); OWSFailDebug(@"Couldn't parse proto.");
// TODO: Add analytics.
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSErrorMessage *errorMessage = [TSErrorMessage corruptedMessageInUnknownThread]; TSErrorMessage *errorMessage = [TSErrorMessage corruptedMessageInUnknownThread];
@ -392,25 +384,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
dispatch_async(self.serialQueue, ^{ dispatch_async(self.serialQueue, ^{
completion(NO); completion(NO);
}); });
return;
}
// Loki: Don't process any messages from ourself
ECKeyPair *_Nullable keyPair = OWSIdentityManager.sharedManager.identityKeyPair;
if (keyPair && [envelope.source isEqualToString:keyPair.hexEncodedPublicKey]) {
dispatch_async(self.serialQueue, ^{
completion(YES);
});
return;
}
// Loki: Ignore any friend requests that we got before restoration
uint64_t restorationTime = [NSNumber numberWithDouble:[OWSPrimaryStorage.sharedManager getRestorationTime]].unsignedLongLongValue;
if (envelope.type == SSKProtoEnvelopeTypeFriendRequest && envelope.timestamp < restorationTime * 1000) {
[LKLogger print:@"[Loki] Ignoring friend request received before restoration."];
dispatch_async(self.serialQueue, ^{
completion(YES);
});
return; return;
} }
@ -481,8 +455,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
// For coherency we use the same dbConnection to persist and read the unprocessed envelopes // For coherency we use the same dbConnection to persist and read the unprocessed envelopes
YapDatabaseConnection *dbConnection = [primaryStorage newDatabaseConnection]; YapDatabaseConnection *dbConnection = [primaryStorage newDatabaseConnection];
OWSMessageDecryptJobFinder *finder = [[OWSMessageDecryptJobFinder alloc] initWithDBConnection:dbConnection]; OWSMessageDecryptJobFinder *finder = [[OWSMessageDecryptJobFinder alloc] initWithDBConnection:dbConnection];
OWSMessageDecryptQueue *processingQueue = OWSMessageDecryptQueue *processingQueue = [[OWSMessageDecryptQueue alloc] initWithDBConnection:dbConnection finder:finder];
[[OWSMessageDecryptQueue alloc] initWithDBConnection:dbConnection finder:finder];
_processingQueue = processingQueue; _processingQueue = processingQueue;
@ -512,7 +485,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
- (void)handleReceivedEnvelopeData:(NSData *)envelopeData - (void)handleReceivedEnvelopeData:(NSData *)envelopeData
{ {
if (envelopeData.length < 1) { if (envelopeData.length < 1) {
OWSFailDebug(@"Empty envelope."); OWSFailDebug(@"Received an empty envelope.");
return; return;
} }
@ -520,7 +493,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
NSUInteger kMaxEnvelopeByteCount = 250 * 1024; NSUInteger kMaxEnvelopeByteCount = 250 * 1024;
if (envelopeData.length > kMaxEnvelopeByteCount) { if (envelopeData.length > kMaxEnvelopeByteCount) {
OWSProdError([OWSAnalyticsEvents messageReceiverErrorOversizeMessage]); OWSProdError([OWSAnalyticsEvents messageReceiverErrorOversizeMessage]);
OWSFailDebug(@"Oversize message."); OWSFailDebug(@"Received an oversized message.");
return; return;
} }
@ -529,7 +502,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
NSUInteger kLargeEnvelopeWarningByteCount = 25 * 1024; NSUInteger kLargeEnvelopeWarningByteCount = 25 * 1024;
if (envelopeData.length > kLargeEnvelopeWarningByteCount) { if (envelopeData.length > kLargeEnvelopeWarningByteCount) {
OWSProdError([OWSAnalyticsEvents messageReceiverErrorLargeMessage]); OWSProdError([OWSAnalyticsEvents messageReceiverErrorLargeMessage]);
OWSFailDebug(@"Unexpectedly large message."); OWSFailDebug(@"Received an unexpectedly large message.");
} }
[self.processingQueue enqueueEnvelopeData:envelopeData]; [self.processingQueue enqueueEnvelopeData:envelopeData];

Loading…
Cancel
Save