From 48ef152c497434e7edaece038f893363356077f6 Mon Sep 17 00:00:00 2001 From: gmbnt Date: Mon, 6 Apr 2020 16:49:45 +1000 Subject: [PATCH] Glue things together again --- Podfile | 3 +- .../xcschemes/SignalMessaging.xcscheme | 67 ++++ Signal/Signal-Info.plist | 2 +- ...iHttpClient.swift => LokiHTTPClient.swift} | 0 .../src/Loki/Crypto/SessionCipher+Loki.m | 320 +++++++++--------- 5 files changed, 230 insertions(+), 162 deletions(-) create mode 100644 Signal.xcodeproj/xcshareddata/xcschemes/SignalMessaging.xcscheme rename SignalServiceKit/src/Loki/API/{LokiHttpClient.swift => LokiHTTPClient.swift} (100%) diff --git a/Podfile b/Podfile index 20a697599..e1988cd15 100644 --- a/Podfile +++ b/Podfile @@ -22,7 +22,8 @@ def shared_pods pod 'Curve25519Kit', git: 'https://github.com/signalapp/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 'SignalServiceKit', path: '.', testspecs: ["Tests"] diff --git a/Signal.xcodeproj/xcshareddata/xcschemes/SignalMessaging.xcscheme b/Signal.xcodeproj/xcshareddata/xcschemes/SignalMessaging.xcscheme new file mode 100644 index 000000000..8362675e7 --- /dev/null +++ b/Signal.xcodeproj/xcshareddata/xcschemes/SignalMessaging.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index 88d659202..fefaeb1ed 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -113,7 +113,7 @@ NSContactsUsageDescription Signal uses your contacts to find users you know. We do not store your contacts on the server. NSFaceIDUsageDescription - Session's Screen Lock feature uses Face ID. + Session's Screen Lock feature uses Face ID. NSMicrophoneUsageDescription Session needs access to your microphone to record videos. NSPhotoLibraryAddUsageDescription diff --git a/SignalServiceKit/src/Loki/API/LokiHttpClient.swift b/SignalServiceKit/src/Loki/API/LokiHTTPClient.swift similarity index 100% rename from SignalServiceKit/src/Loki/API/LokiHttpClient.swift rename to SignalServiceKit/src/Loki/API/LokiHTTPClient.swift diff --git a/SignalServiceKit/src/Loki/Crypto/SessionCipher+Loki.m b/SignalServiceKit/src/Loki/Crypto/SessionCipher+Loki.m index 467fe63a2..06e6b2e34 100644 --- a/SignalServiceKit/src/Loki/Crypto/SessionCipher+Loki.m +++ b/SignalServiceKit/src/Loki/Crypto/SessionCipher+Loki.m @@ -1,160 +1,160 @@ -// Loki: Refer to Docs/SessionReset.md for explanations - -#import "SessionCipher+Loki.h" -#import "NSNotificationCenter+OWS.h" -#import "PreKeyWhisperMessage.h" -#import "OWSPrimaryStorage+Loki.h" -#import "TSContactThread.h" -#import - -NSString *const kNSNotificationName_SessionAdopted = @"kNSNotificationName_SessionAdopted"; -NSString *const kNSNotificationKey_ContactPubKey = @"kNSNotificationKey_ContactPubKey"; - -@interface SessionCipher () - -@property (nonatomic, readonly) NSString *recipientId; -@property (nonatomic, readonly) int deviceId; - -@property (nonatomic, readonly) id sessionStore; -@property (nonatomic, readonly) id prekeyStore; - -@end - -@implementation SessionCipher (Loki) - -- (NSData *)throws_lokiDecrypt:(id)whisperMessage protocolContext:(nullable id)protocolContext -{ - // Our state before we decrypt the message - SessionState *_Nullable state = [self getCurrentState:protocolContext]; - - // Verify incoming friend request messages - if (!state) { - [self throws_validatePreKeysForFriendRequestAcceptance:whisperMessage protocolContext:protocolContext]; - } - - // While decrypting our state may change internally - NSData *plainText = [self throws_decrypt:whisperMessage protocolContext:protocolContext]; - - - // Loki: Handle any session resets - [self handleSessionReset:whisperMessage previousState:state protocolContext:protocolContext]; - - return plainText; -} - -/// Get the current session state -- (SessionState *_Nullable)getCurrentState:(nullable id)protocolContext { - SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext]; - SessionState *state = record.sessionState; - - // Check if session is initialized - if (!state.hasSenderChain) { return nil; } - - return state; -} - -/// Handle any Loki session reset stuff -- (void)handleSessionReset:(id)whisperMessage previousState:(SessionState *_Nullable)previousState protocolContext:(nullable id)protocolContext -{ - // Don't bother doing anything if we didn't have a session before - if (!previousState) { return; } - - OWSAssertDebug([protocolContext isKindOfClass:[YapDatabaseReadWriteTransaction class]]); - YapDatabaseReadWriteTransaction *transaction = protocolContext; - - // Get the thread - TSContactThread *thread = [TSContactThread getThreadWithContactId:self.recipientId transaction:transaction]; - if (!thread) { return; } - - // Bail early if no session reset is in progress - if (thread.sessionResetState == TSContactThreadSessionResetStateNone) { return; } - - BOOL sessionResetReceived = thread.sessionResetState == TSContactThreadSessionResetStateRequestReceived; - SessionState *_Nullable currentState = [self getCurrentState:protocolContext]; - - // Check if our previous state and our current state differ - if (!currentState || ![currentState.aliceBaseKey isEqualToData:previousState.aliceBaseKey]) { - - if (sessionResetReceived) { - // The other user used an old session to contact us. - // Wait for them to use a new one - [self restoreSession:previousState protocolContext:protocolContext]; - } else { - // Our session reset went through successfully - // We had initiated a session reset and got a different session back from the user - [self deleteAllSessionsExcept:currentState protocolContext:protocolContext]; - [self notifySessionAdopted]; - } - - } else if (sessionResetReceived) { - // Our session reset went through successfully - // We got a message with the same session from the other user - [self deleteAllSessionsExcept:previousState protocolContext:protocolContext]; - [self notifySessionAdopted]; - } -} - -/// Send a notification about a new session being adopted -- (void)notifySessionAdopted -{ - NSDictionary *userInfo = @{ kNSNotificationKey_ContactPubKey : self.recipientId }; - [NSNotificationCenter.defaultCenter postNotificationNameAsync:kNSNotificationName_SessionAdopted object:nil userInfo:userInfo]; -} - -/// Delete all other sessions except the given one -- (void)deleteAllSessionsExcept:(SessionState *)state protocolContext:(nullable id)protocolContext -{ - SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext]; - [record removePreviousSessionStates]; - - SessionState *newState = state == nil ? [SessionState new] : state; - [record setState:newState]; - - [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 -- (void)restoreSession:(SessionState *)state protocolContext:(nullable id)protocolContext -{ - SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext]; - - // Remove the state from previous session states - [record.previousSessionStates enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(SessionState *obj, NSUInteger idx, BOOL *stop) { - if ([state.aliceBaseKey isEqualToData:obj.aliceBaseKey]) { - [record.previousSessionStates removeObjectAtIndex:idx]; - *stop = YES; - } - }]; - - // Promote it so the previous state gets archived - [record promoteState:state]; - - [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`. -/// This is so that we don't trigger a false friend request accept on unknown contacts. -- (void)throws_validatePreKeysForFriendRequestAcceptance:(id)whisperMessage protocolContext:(nullable id)protocolContext { - OWSAssertDebug([protocolContext isKindOfClass:[YapDatabaseReadTransaction class]]); - YapDatabaseReadTransaction *transaction = protocolContext; - - // Ignore anything that isn't a `PreKeyWhisperMessage` - if (![whisperMessage isKindOfClass:[PreKeyWhisperMessage class]]) { return; } - - // Check the pre key store - if (![self.prekeyStore isKindOfClass:[OWSPrimaryStorage class]]) { return; } - - PreKeyWhisperMessage *preKeyMessage = whisperMessage; - OWSPrimaryStorage *primaryStorage = self.prekeyStore; - - PreKeyRecord *_Nullable storedPreKey = [primaryStorage getPreKeyForContact:self.recipientId transaction:transaction]; - if (!storedPreKey) { - OWSRaiseException(@"Loki", @"Received a friend request from a public key for which no pre key bundle was created."); - } - - if (storedPreKey.Id != preKeyMessage.prekeyID) { - OWSRaiseException(@"Loki", @"Received a PreKeyWhisperMessage (friend request accept) from an unknown source."); - } -} - -@end +//// Loki: Refer to Docs/SessionReset.md for explanations +// +//#import "SessionCipher+Loki.h" +//#import "NSNotificationCenter+OWS.h" +//#import "PreKeyWhisperMessage.h" +//#import "OWSPrimaryStorage+Loki.h" +//#import "TSContactThread.h" +//#import +// +//NSString *const kNSNotificationName_SessionAdopted = @"kNSNotificationName_SessionAdopted"; +//NSString *const kNSNotificationKey_ContactPubKey = @"kNSNotificationKey_ContactPubKey"; +// +//@interface SessionCipher () +// +//@property (nonatomic, readonly) NSString *recipientId; +//@property (nonatomic, readonly) int deviceId; +// +//@property (nonatomic, readonly) id sessionStore; +//@property (nonatomic, readonly) id prekeyStore; +// +//@end +// +//@implementation SessionCipher (Loki) +// +//- (NSData *)throws_lokiDecrypt:(id)whisperMessage protocolContext:(nullable id)protocolContext +//{ +// // Our state before we decrypt the message +// SessionState *_Nullable state = [self getCurrentState:protocolContext]; +// +// // Verify incoming friend request messages +// if (!state) { +// [self throws_validatePreKeysForFriendRequestAcceptance:whisperMessage protocolContext:protocolContext]; +// } +// +// // While decrypting our state may change internally +// NSData *plainText = [self throws_decrypt:whisperMessage protocolContext:protocolContext]; +// +// +// // Loki: Handle any session resets +// [self handleSessionReset:whisperMessage previousState:state protocolContext:protocolContext]; +// +// return plainText; +//} +// +///// Get the current session state +//- (SessionState *_Nullable)getCurrentState:(nullable id)protocolContext { +// SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext]; +// SessionState *state = record.sessionState; +// +// // Check if session is initialized +// if (!state.hasSenderChain) { return nil; } +// +// return state; +//} +// +///// Handle any Loki session reset stuff +//- (void)handleSessionReset:(id)whisperMessage previousState:(SessionState *_Nullable)previousState protocolContext:(nullable id)protocolContext +//{ +// // Don't bother doing anything if we didn't have a session before +// if (!previousState) { return; } +// +// OWSAssertDebug([protocolContext isKindOfClass:[YapDatabaseReadWriteTransaction class]]); +// YapDatabaseReadWriteTransaction *transaction = protocolContext; +// +// // Get the thread +// TSContactThread *thread = [TSContactThread getThreadWithContactId:self.recipientId transaction:transaction]; +// if (!thread) { return; } +// +// // Bail early if no session reset is in progress +// if (thread.sessionResetState == TSContactThreadSessionResetStateNone) { return; } +// +// BOOL sessionResetReceived = thread.sessionResetState == TSContactThreadSessionResetStateRequestReceived; +// SessionState *_Nullable currentState = [self getCurrentState:protocolContext]; +// +// // Check if our previous state and our current state differ +// if (!currentState || ![currentState.aliceBaseKey isEqualToData:previousState.aliceBaseKey]) { +// +// if (sessionResetReceived) { +// // The other user used an old session to contact us. +// // Wait for them to use a new one +// [self restoreSession:previousState protocolContext:protocolContext]; +// } else { +// // Our session reset went through successfully +// // We had initiated a session reset and got a different session back from the user +// [self deleteAllSessionsExcept:currentState protocolContext:protocolContext]; +// [self notifySessionAdopted]; +// } +// +// } else if (sessionResetReceived) { +// // Our session reset went through successfully +// // We got a message with the same session from the other user +// [self deleteAllSessionsExcept:previousState protocolContext:protocolContext]; +// [self notifySessionAdopted]; +// } +//} +// +///// Send a notification about a new session being adopted +//- (void)notifySessionAdopted +//{ +// NSDictionary *userInfo = @{ kNSNotificationKey_ContactPubKey : self.recipientId }; +// [NSNotificationCenter.defaultCenter postNotificationNameAsync:kNSNotificationName_SessionAdopted object:nil userInfo:userInfo]; +//} +// +///// Delete all other sessions except the given one +//- (void)deleteAllSessionsExcept:(SessionState *)state protocolContext:(nullable id)protocolContext +//{ +// SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext]; +// [record removePreviousSessionStates]; +// +// SessionState *newState = state == nil ? [SessionState new] : state; +// [record setState:newState]; +// +// [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 +//- (void)restoreSession:(SessionState *)state protocolContext:(nullable id)protocolContext +//{ +// SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext]; +// +// // Remove the state from previous session states +// [record.previousSessionStates enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(SessionState *obj, NSUInteger idx, BOOL *stop) { +// if ([state.aliceBaseKey isEqualToData:obj.aliceBaseKey]) { +// [record.previousSessionStates removeObjectAtIndex:idx]; +// *stop = YES; +// } +// }]; +// +// // Promote it so the previous state gets archived +// [record promoteState:state]; +// +// [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`. +///// This is so that we don't trigger a false friend request accept on unknown contacts. +//- (void)throws_validatePreKeysForFriendRequestAcceptance:(id)whisperMessage protocolContext:(nullable id)protocolContext { +// OWSAssertDebug([protocolContext isKindOfClass:[YapDatabaseReadTransaction class]]); +// YapDatabaseReadTransaction *transaction = protocolContext; +// +// // Ignore anything that isn't a `PreKeyWhisperMessage` +// if (![whisperMessage isKindOfClass:[PreKeyWhisperMessage class]]) { return; } +// +// // Check the pre key store +// if (![self.prekeyStore isKindOfClass:[OWSPrimaryStorage class]]) { return; } +// +// PreKeyWhisperMessage *preKeyMessage = whisperMessage; +// OWSPrimaryStorage *primaryStorage = self.prekeyStore; +// +// PreKeyRecord *_Nullable storedPreKey = [primaryStorage getPreKeyForContact:self.recipientId transaction:transaction]; +// if (!storedPreKey) { +// OWSRaiseException(@"Loki", @"Received a friend request from a public key for which no pre key bundle was created."); +// } +// +// if (storedPreKey.Id != preKeyMessage.prekeyID) { +// OWSRaiseException(@"Loki", @"Received a PreKeyWhisperMessage (friend request accept) from an unknown source."); +// } +//} +// +//@end