Glue things together again
This commit is contained in:
parent
03c4e4a65c
commit
48ef152c49
3
Podfile
3
Podfile
|
@ -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"]
|
||||||
|
|
|
@ -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>
|
|
@ -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'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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue