Glue things together again

This commit is contained in:
gmbnt 2020-04-06 16:49:45 +10:00
parent 03c4e4a65c
commit 48ef152c49
5 changed files with 230 additions and 162 deletions

View File

@ -22,7 +22,8 @@ def shared_pods
pod 'Curve25519Kit', git: 'https://github.com/signalapp/Curve25519Kit', testspecs: ["Tests"] pod 'Curve25519Kit', git: 'https://github.com/signalapp/Curve25519Kit', testspecs: ["Tests"]
# pod 'Curve25519Kit', path: '../Curve25519Kit', testspecs: ["Tests"] # pod 'Curve25519Kit', path: '../Curve25519Kit', testspecs: ["Tests"]
pod 'SignalMetadataKit', git: 'https://github.com/signalapp/SignalMetadataKit', testspecs: ["Tests"] # Don't update SignalMetadataKit. There's some Loki specific stuff in there that gets overwritten otherwise.
# pod 'SignalMetadataKit', git: 'https://github.com/signalapp/SignalMetadataKit', testspecs: ["Tests"]
# pod 'SignalMetadataKit', path: '../SignalMetadataKit', testspecs: ["Tests"] # pod 'SignalMetadataKit', path: '../SignalMetadataKit', testspecs: ["Tests"]
pod 'SignalServiceKit', path: '.', testspecs: ["Tests"] pod 'SignalServiceKit', path: '.', testspecs: ["Tests"]

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1140"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "453518911FC63DBF00210559"
BuildableName = "SignalMessaging.framework"
BlueprintName = "SignalMessaging"
ReferencedContainer = "container:Signal.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "App Store Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "453518911FC63DBF00210559"
BuildableName = "SignalMessaging.framework"
BlueprintName = "SignalMessaging"
ReferencedContainer = "container:Signal.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "App Store Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -113,7 +113,7 @@
<key>NSContactsUsageDescription</key> <key>NSContactsUsageDescription</key>
<string>Signal uses your contacts to find users you know. We do not store your contacts on the server.</string> <string>Signal uses your contacts to find users you know. We do not store your contacts on the server.</string>
<key>NSFaceIDUsageDescription</key> <key>NSFaceIDUsageDescription</key>
<string>Session&apos;s Screen Lock feature uses Face ID.</string> <string>Session's Screen Lock feature uses Face ID.</string>
<key>NSMicrophoneUsageDescription</key> <key>NSMicrophoneUsageDescription</key>
<string>Session needs access to your microphone to record videos.</string> <string>Session needs access to your microphone to record videos.</string>
<key>NSPhotoLibraryAddUsageDescription</key> <key>NSPhotoLibraryAddUsageDescription</key>

View File

@ -1,160 +1,160 @@
// Loki: Refer to Docs/SessionReset.md for explanations //// Loki: Refer to Docs/SessionReset.md for explanations
//
#import "SessionCipher+Loki.h" //#import "SessionCipher+Loki.h"
#import "NSNotificationCenter+OWS.h" //#import "NSNotificationCenter+OWS.h"
#import "PreKeyWhisperMessage.h" //#import "PreKeyWhisperMessage.h"
#import "OWSPrimaryStorage+Loki.h" //#import "OWSPrimaryStorage+Loki.h"
#import "TSContactThread.h" //#import "TSContactThread.h"
#import <YapDatabase/YapDatabase.h> //#import <YapDatabase/YapDatabase.h>
//
NSString *const kNSNotificationName_SessionAdopted = @"kNSNotificationName_SessionAdopted"; //NSString *const kNSNotificationName_SessionAdopted = @"kNSNotificationName_SessionAdopted";
NSString *const kNSNotificationKey_ContactPubKey = @"kNSNotificationKey_ContactPubKey"; //NSString *const kNSNotificationKey_ContactPubKey = @"kNSNotificationKey_ContactPubKey";
//
@interface SessionCipher () //@interface SessionCipher ()
//
@property (nonatomic, readonly) NSString *recipientId; //@property (nonatomic, readonly) NSString *recipientId;
@property (nonatomic, readonly) int deviceId; //@property (nonatomic, readonly) int deviceId;
//
@property (nonatomic, readonly) id<SessionStore> sessionStore; //@property (nonatomic, readonly) id<SessionStore> sessionStore;
@property (nonatomic, readonly) id<PreKeyStore> prekeyStore; //@property (nonatomic, readonly) id<PreKeyStore> prekeyStore;
//
@end //@end
//
@implementation SessionCipher (Loki) //@implementation SessionCipher (Loki)
//
- (NSData *)throws_lokiDecrypt:(id<CipherMessage>)whisperMessage protocolContext:(nullable id)protocolContext //- (NSData *)throws_lokiDecrypt:(id<CipherMessage>)whisperMessage protocolContext:(nullable id)protocolContext
{ //{
// 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];
//
// Verify incoming friend request messages // // Verify incoming friend request messages
if (!state) { // if (!state) {
[self throws_validatePreKeysForFriendRequestAcceptance:whisperMessage protocolContext:protocolContext]; // [self throws_validatePreKeysForFriendRequestAcceptance:whisperMessage protocolContext:protocolContext];
} // }
//
// While decrypting our state may change internally // // While decrypting our state may change internally
NSData *plainText = [self throws_decrypt:whisperMessage protocolContext:protocolContext]; // NSData *plainText = [self throws_decrypt:whisperMessage protocolContext:protocolContext];
//
//
// Loki: Handle any session resets // // Loki: Handle any session resets
[self handleSessionReset:whisperMessage previousState:state protocolContext:protocolContext]; // [self handleSessionReset:whisperMessage previousState:state protocolContext:protocolContext];
//
return plainText; // return plainText;
} //}
//
/// Get the current session state ///// Get the current session state
- (SessionState *_Nullable)getCurrentState:(nullable id)protocolContext { //- (SessionState *_Nullable)getCurrentState:(nullable id)protocolContext {
SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext]; // SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
SessionState *state = record.sessionState; // SessionState *state = record.sessionState;
//
// Check if session is initialized // // Check if session is initialized
if (!state.hasSenderChain) { return nil; } // if (!state.hasSenderChain) { return nil; }
//
return state; // return state;
} //}
//
/// Handle any Loki session reset stuff ///// Handle any Loki session reset stuff
- (void)handleSessionReset:(id<CipherMessage>)whisperMessage previousState:(SessionState *_Nullable)previousState protocolContext:(nullable id)protocolContext //- (void)handleSessionReset:(id<CipherMessage>)whisperMessage previousState:(SessionState *_Nullable)previousState protocolContext:(nullable id)protocolContext
{ //{
// Don't bother doing anything if we didn't have a session before // // Don't bother doing anything if we didn't have a session before
if (!previousState) { return; } // if (!previousState) { return; }
//
OWSAssertDebug([protocolContext isKindOfClass:[YapDatabaseReadWriteTransaction class]]); // OWSAssertDebug([protocolContext isKindOfClass:[YapDatabaseReadWriteTransaction class]]);
YapDatabaseReadWriteTransaction *transaction = protocolContext; // YapDatabaseReadWriteTransaction *transaction = protocolContext;
//
// Get the thread // // Get the thread
TSContactThread *thread = [TSContactThread getThreadWithContactId:self.recipientId transaction:transaction]; // TSContactThread *thread = [TSContactThread getThreadWithContactId:self.recipientId transaction:transaction];
if (!thread) { return; } // if (!thread) { return; }
//
// Bail early if no session reset is in progress // // Bail early if no session reset is in progress
if (thread.sessionResetState == TSContactThreadSessionResetStateNone) { return; } // if (thread.sessionResetState == TSContactThreadSessionResetStateNone) { return; }
//
BOOL sessionResetReceived = thread.sessionResetState == TSContactThreadSessionResetStateRequestReceived; // BOOL sessionResetReceived = thread.sessionResetState == TSContactThreadSessionResetStateRequestReceived;
SessionState *_Nullable currentState = [self getCurrentState:protocolContext]; // SessionState *_Nullable currentState = [self getCurrentState:protocolContext];
//
// Check if our previous state and our current state differ // // Check if our previous state and our current state differ
if (!currentState || ![currentState.aliceBaseKey isEqualToData:previousState.aliceBaseKey]) { // if (!currentState || ![currentState.aliceBaseKey isEqualToData:previousState.aliceBaseKey]) {
//
if (sessionResetReceived) { // if (sessionResetReceived) {
// The other user used an old session to contact us. // // The other user used an old session to contact us.
// Wait for them to use a new one // // Wait for them to use a new one
[self restoreSession:previousState protocolContext:protocolContext]; // [self restoreSession:previousState protocolContext:protocolContext];
} else { // } else {
// Our session reset went through successfully // // Our session reset went through successfully
// We had initiated a session reset and got a different session back from the user // // We had initiated a session reset and got a different session back from the user
[self deleteAllSessionsExcept:currentState protocolContext:protocolContext]; // [self deleteAllSessionsExcept:currentState protocolContext:protocolContext];
[self notifySessionAdopted]; // [self notifySessionAdopted];
} // }
//
} else if (sessionResetReceived) { // } else if (sessionResetReceived) {
// Our session reset went through successfully // // Our session reset went through successfully
// We got a message with the same session from the other user // // We got a message with the same session from the other user
[self deleteAllSessionsExcept:previousState protocolContext:protocolContext]; // [self deleteAllSessionsExcept:previousState protocolContext:protocolContext];
[self notifySessionAdopted]; // [self notifySessionAdopted];
} // }
} //}
//
/// Send a notification about a new session being adopted ///// Send a notification about a new session being adopted
- (void)notifySessionAdopted //- (void)notifySessionAdopted
{ //{
NSDictionary *userInfo = @{ kNSNotificationKey_ContactPubKey : self.recipientId }; // NSDictionary *userInfo = @{ kNSNotificationKey_ContactPubKey : self.recipientId };
[NSNotificationCenter.defaultCenter postNotificationNameAsync:kNSNotificationName_SessionAdopted object:nil userInfo:userInfo]; // [NSNotificationCenter.defaultCenter postNotificationNameAsync:kNSNotificationName_SessionAdopted object:nil userInfo:userInfo];
} //}
//
/// Delete all other sessions except the given one ///// Delete all other sessions except the given one
- (void)deleteAllSessionsExcept:(SessionState *)state protocolContext:(nullable id)protocolContext //- (void)deleteAllSessionsExcept:(SessionState *)state protocolContext:(nullable id)protocolContext
{ //{
SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext]; // SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
[record removePreviousSessionStates]; // [record removePreviousSessionStates];
//
SessionState *newState = state == nil ? [SessionState new] : state; // SessionState *newState = state == nil ? [SessionState new] : state;
[record setState:newState]; // [record setState:newState];
//
[self.sessionStore storeSession:self.recipientId deviceId:self.deviceId session:record protocolContext:protocolContext]; // [self.sessionStore storeSession:self.recipientId deviceId:self.deviceId session:record protocolContext:protocolContext];
} //}
//
/// Set the given session as the active one while archiving the old one ///// Set the given session as the active one while archiving the old one
- (void)restoreSession:(SessionState *)state protocolContext:(nullable id)protocolContext //- (void)restoreSession:(SessionState *)state protocolContext:(nullable id)protocolContext
{ //{
SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext]; // SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
//
// Remove the state from previous session states // // Remove the state from previous session states
[record.previousSessionStates enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(SessionState *obj, NSUInteger idx, BOOL *stop) { // [record.previousSessionStates enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(SessionState *obj, NSUInteger idx, BOOL *stop) {
if ([state.aliceBaseKey isEqualToData:obj.aliceBaseKey]) { // if ([state.aliceBaseKey isEqualToData:obj.aliceBaseKey]) {
[record.previousSessionStates removeObjectAtIndex:idx]; // [record.previousSessionStates removeObjectAtIndex:idx];
*stop = YES; // *stop = YES;
} // }
}]; // }];
//
// Promote it so the previous state gets archived // // Promote it so the previous state gets archived
[record promoteState:state]; // [record promoteState:state];
//
[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 pre keys 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_validatePreKeysForFriendRequestAcceptance:(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;
//
// Ignore anything that isn't a `PreKeyWhisperMessage` // // Ignore anything that isn't a `PreKeyWhisperMessage`
if (![whisperMessage isKindOfClass:[PreKeyWhisperMessage class]]) { return; } // if (![whisperMessage isKindOfClass:[PreKeyWhisperMessage class]]) { return; }
//
// Check the pre key store // // 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;
OWSPrimaryStorage *primaryStorage = self.prekeyStore; // OWSPrimaryStorage *primaryStorage = self.prekeyStore;
//
PreKeyRecord *_Nullable storedPreKey = [primaryStorage getPreKeyForContact:self.recipientId transaction:transaction]; // PreKeyRecord *_Nullable storedPreKey = [primaryStorage getPreKeyForContact:self.recipientId transaction:transaction];
if (!storedPreKey) { // if (!storedPreKey) {
OWSRaiseException(@"Loki", @"Received a friend request from a public key for which no pre key 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(@"Loki", @"Received a PreKeyWhisperMessage (friend request accept) from an unknown source."); // OWSRaiseException(@"Loki", @"Received a PreKeyWhisperMessage (friend request accept) from an unknown source.");
} // }
} //}
//
@end //@end