From 70e536ca8f81fbd3e0e3b4be9954717d10768717 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 7 Nov 2016 20:42:44 -0500 Subject: [PATCH] Privacy preferences for blocking identity change // FREEBIE --- Example/TSKitiOSTestApp/Podfile.lock | 4 +- SignalServiceKit.podspec | 2 +- src/Account/TSPrivacyPreferences.h | 16 +++++++ src/Account/TSPrivacyPreferences.m | 45 +++++++++++++++++++ src/Messages/Interactions/TSErrorMessage.h | 1 + src/Messages/Interactions/TSErrorMessage.m | 2 + src/Protocols/UserPreferences.h | 13 ------ .../TSStorageManager+IdentityKeyStore.m | 32 +++++++++++-- src/Storage/TSStorageManager.h | 2 + src/Storage/TSStorageManager.m | 17 ++++--- src/TextSecureKitEnv.h | 2 +- .../Storage/TSStorageIdentityKeyStoreTests.m | 33 ++++++++++++-- 12 files changed, 140 insertions(+), 29 deletions(-) create mode 100644 src/Account/TSPrivacyPreferences.h create mode 100644 src/Account/TSPrivacyPreferences.m delete mode 100644 src/Protocols/UserPreferences.h diff --git a/Example/TSKitiOSTestApp/Podfile.lock b/Example/TSKitiOSTestApp/Podfile.lock index 0e451d178..e11046685 100644 --- a/Example/TSKitiOSTestApp/Podfile.lock +++ b/Example/TSKitiOSTestApp/Podfile.lock @@ -35,7 +35,7 @@ PODS: - ProtocolBuffers (1.9.10) - Reachability (3.2) - SAMKeychain (1.5.0) - - SignalServiceKit (0.4.0): + - SignalServiceKit (0.6.0): - '25519' - AFNetworking - AxolotlKit @@ -130,7 +130,7 @@ SPEC CHECKSUMS: ProtocolBuffers: d088180c10072b3d24a9939a6314b7b9bcc2340b Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 SAMKeychain: 1fc9ae02f576365395758b12888c84704eebc423 - SignalServiceKit: 5c3877241082a778c8c130e1fed17d0904085205 + SignalServiceKit: c580eb2197f87212fcba9f7faf56163f410225e9 SocketRocket: 3f77ec2104cc113add553f817ad90a77114f5d43 SQLCipher: 4c768761421736a247ed6cf412d9045615d53dff TwistedOakCollapsingFutures: f359b90f203e9ab13dfb92c9ff41842a7fe1cd0c diff --git a/SignalServiceKit.podspec b/SignalServiceKit.podspec index a8c76005e..1d2d18b40 100644 --- a/SignalServiceKit.podspec +++ b/SignalServiceKit.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = "SignalServiceKit" - s.version = "0.5.3" + s.version = "0.6.0" s.summary = "An Objective-C library for communicating with the Signal messaging service." s.description = <<-DESC diff --git a/src/Account/TSPrivacyPreferences.h b/src/Account/TSPrivacyPreferences.h new file mode 100644 index 000000000..c334db853 --- /dev/null +++ b/src/Account/TSPrivacyPreferences.h @@ -0,0 +1,16 @@ +// Created by Michael Kirk on 11/7/16. +// Copyright © 2016 Open Whisper Systems. All rights reserved. + +#import "TSYapDatabaseObject.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TSPrivacyPreferences : TSYapDatabaseObject + ++ (instancetype)sharedInstance; + +@property BOOL shouldBlockOnIdentityChange; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/Account/TSPrivacyPreferences.m b/src/Account/TSPrivacyPreferences.m new file mode 100644 index 000000000..3be8d8621 --- /dev/null +++ b/src/Account/TSPrivacyPreferences.m @@ -0,0 +1,45 @@ +// Created by Michael Kirk on 11/7/16. +// Copyright © 2016 Open Whisper Systems. All rights reserved. + +#import "TSPrivacyPreferences.h" + +NS_ASSUME_NONNULL_BEGIN + +NSString *const TSPrivacyPreferencesSingletonKey = @"TSPrivacyPreferences"; + +@implementation TSPrivacyPreferences + ++ (instancetype)sharedInstance +{ + static TSPrivacyPreferences *sharedInstance; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [self fetchObjectWithUniqueID:TSPrivacyPreferencesSingletonKey]; + if (!sharedInstance) { + sharedInstance = [[self alloc] initDefault]; + } + }); + + return sharedInstance; +} + +- (instancetype)initDefault +{ + return [self initWithShouldBlockOnIdentityChange:NO]; +} + +- (instancetype)initWithShouldBlockOnIdentityChange:(BOOL)shouldBlockOnIdentityChange +{ + self = [super initWithUniqueId:TSPrivacyPreferencesSingletonKey]; + if (!self) { + return self; + } + + _shouldBlockOnIdentityChange = shouldBlockOnIdentityChange; + + return self; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/Messages/Interactions/TSErrorMessage.h b/src/Messages/Interactions/TSErrorMessage.h index 9cfc90106..b03c7f3f6 100644 --- a/src/Messages/Interactions/TSErrorMessage.h +++ b/src/Messages/Interactions/TSErrorMessage.h @@ -14,6 +14,7 @@ typedef NS_ENUM(int32_t, TSErrorMessageType) { TSErrorMessageInvalidMessage, TSErrorMessageDuplicateMessage, TSErrorMessageInvalidVersion, + TSErrorMessageNonBlockingIdentityChange, }; -(instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; diff --git a/src/Messages/Interactions/TSErrorMessage.m b/src/Messages/Interactions/TSErrorMessage.m index e381932a6..78e08faad 100644 --- a/src/Messages/Interactions/TSErrorMessage.m +++ b/src/Messages/Interactions/TSErrorMessage.m @@ -65,6 +65,8 @@ return NSLocalizedString(@"ERROR_MESSAGE_INVALID_KEY_EXCEPTION", @""); case TSErrorMessageWrongTrustedIdentityKey: return NSLocalizedString(@"ERROR_MESSAGE_WRONG_TRUSTED_IDENTITY_KEY", @""); + case TSErrorMessageNonBlockingIdentityChange: + return NSLocalizedString(@"ERROR_MESSAGE_NON_BLOCKING_IDENTITY_CHANGE", @""); default: return NSLocalizedString(@"ERROR_MESSAGE_UNKNOWN_ERROR", @""); break; diff --git a/src/Protocols/UserPreferences.h b/src/Protocols/UserPreferences.h deleted file mode 100644 index fedc4d935..000000000 --- a/src/Protocols/UserPreferences.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// UserPreferences.h -// Pods -// -// Created by Frederic Jacobs on 05/12/15. -// -// - -#import - -@protocol UserPreferencesProtocol - -@end diff --git a/src/Storage/AxolotlStore/TSStorageManager+IdentityKeyStore.m b/src/Storage/AxolotlStore/TSStorageManager+IdentityKeyStore.m index e7c1fe166..36e2ca400 100644 --- a/src/Storage/AxolotlStore/TSStorageManager+IdentityKeyStore.m +++ b/src/Storage/AxolotlStore/TSStorageManager+IdentityKeyStore.m @@ -1,9 +1,12 @@ // Created by Frederic Jacobs on 06/11/14. // Copyright (c) 2014 Open Whisper Systems. All rights reserved. +#import "NSDate+millisecondTimeStamp.h" #import "TSAccountManager.h" +#import "TSContactThread.h" +#import "TSErrorMessage.h" +#import "TSPrivacyPreferences.h" #import "TSStorageManager+IdentityKeyStore.h" - #import <25519/Curve25519.h> #define TSStorageManagerIdentityKeyStoreIdentityKey \ @@ -40,13 +43,36 @@ } - (BOOL)isTrustedIdentityKey:(NSData *)identityKey recipientId:(NSString *)recipientId { - NSData *trusted = [self dataForKey:recipientId inCollection:TSStorageManagerTrustedKeysCollection]; + NSData *existingKey = [self dataForKey:recipientId inCollection:TSStorageManagerTrustedKeysCollection]; + + if (!existingKey) { + return YES; + } + + if ([existingKey isEqualToData:identityKey]) { + return YES; + } - return (trusted == nil || [trusted isEqualToData:identityKey]); + if (self.privacyPreferences.shouldBlockOnIdentityChange) { + return NO; + } + + DDLogInfo(@"Updating identity key for recipient:%@", recipientId); + [self createIdentityChangeInfoMessageForRecipientId:recipientId]; + [self saveRemoteIdentity:identityKey recipientId:recipientId]; + return YES; } - (void)removeIdentityKeyForRecipient:(NSString *)receipientId { [self removeObjectForKey:receipientId inCollection:TSStorageManagerTrustedKeysCollection]; } +- (void)createIdentityChangeInfoMessageForRecipientId:(NSString *)recipientId +{ + TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:recipientId]; + [[[TSErrorMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + inThread:contactThread + failedMessageType:TSErrorMessageNonBlockingIdentityChange] save]; +} + @end diff --git a/src/Storage/TSStorageManager.h b/src/Storage/TSStorageManager.h index f9bd5ed0c..50f29d4fe 100644 --- a/src/Storage/TSStorageManager.h +++ b/src/Storage/TSStorageManager.h @@ -14,6 +14,7 @@ @class ECKeyPair; @class PreKeyRecord; @class SignedPreKeyRecord; +@class TSPrivacyPreferences; extern NSString *const TSUIDatabaseConnectionDidUpdateNotification; @@ -46,5 +47,6 @@ extern NSString *const TSUIDatabaseConnectionDidUpdateNotification; - (void)purgeCollection:(NSString *)collection; @property (nonatomic, readonly) YapDatabaseConnection *dbConnection; +@property (nonatomic, readonly) TSPrivacyPreferences *privacyPreferences; @end diff --git a/src/Storage/TSStorageManager.m b/src/Storage/TSStorageManager.m index 2e218e5e4..36fa6ae58 100644 --- a/src/Storage/TSStorageManager.m +++ b/src/Storage/TSStorageManager.m @@ -10,6 +10,7 @@ #import "TSDatabaseSecondaryIndexes.h" #import "TSDatabaseView.h" #import "TSInteraction.h" +#import "TSPrivacyPreferences.h" #import "TSThread.h" #import <25519/Randomness.h> #import @@ -73,18 +74,19 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass"; @implementation TSStorageManager + (instancetype)sharedManager { - static TSStorageManager *sharedMyManager = nil; + static TSStorageManager *sharedManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - sharedMyManager = [[self alloc] init]; + sharedManager = [[self alloc] initDefault]; #if TARGET_OS_IPHONE - [sharedMyManager protectSignalFiles]; + [sharedManager protectSignalFiles]; #endif }); - return sharedMyManager; + return sharedManager; } -- (instancetype)init { +- (instancetype)initDefault +{ self = [super init]; YapDatabaseOptions *options = [[YapDatabaseOptions alloc] init]; @@ -178,6 +180,11 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass"; return self.database.newConnection; } +- (TSPrivacyPreferences *)privacyPreferences +{ + return [TSPrivacyPreferences sharedInstance]; +} + - (BOOL)userSetPassword { return FALSE; } diff --git a/src/TextSecureKitEnv.h b/src/TextSecureKitEnv.h index 59b954686..e19c36a8a 100644 --- a/src/TextSecureKitEnv.h +++ b/src/TextSecureKitEnv.h @@ -18,4 +18,4 @@ + (instancetype)sharedEnv; -@end \ No newline at end of file +@end diff --git a/tests/Storage/TSStorageIdentityKeyStoreTests.m b/tests/Storage/TSStorageIdentityKeyStoreTests.m index 84a0f301f..341d781f8 100644 --- a/tests/Storage/TSStorageIdentityKeyStoreTests.m +++ b/tests/Storage/TSStorageIdentityKeyStoreTests.m @@ -9,9 +9,10 @@ #import #import <25519/Curve25519.h> -#import "TSStorageManager.h" -#import "TSStorageManager+IdentityKeyStore.h" #import "SecurityUtils.h" +#import "TSPrivacyPreferences.h" +#import "TSStorageManager+IdentityKeyStore.h" +#import "TSStorageManager.h" @interface TSStorageIdentityKeyStoreTests : XCTestCase @@ -47,10 +48,15 @@ } -- (void)testChangedKey { +- (void)testChangedKeyWithBlockingIdentityChanges +{ + TSPrivacyPreferences *preferences = [TSPrivacyPreferences sharedInstance]; + preferences.shouldBlockOnIdentityChange = YES; + [preferences save]; + NSData *newKey = [SecurityUtils generateRandomBytes:32]; NSString *recipientId = @"test@gmail.com"; - + [[TSStorageManager sharedManager] saveRemoteIdentity:newKey recipientId:recipientId]; XCTAssert([[TSStorageManager sharedManager] isTrustedIdentityKey:newKey recipientId:recipientId]); @@ -60,6 +66,25 @@ XCTAssertFalse([[TSStorageManager sharedManager] isTrustedIdentityKey:otherKey recipientId:recipientId]); } + +- (void)testChangedKeyWIthNonBlockingIdentityChanges +{ + TSPrivacyPreferences *preferences = [TSPrivacyPreferences sharedInstance]; + preferences.shouldBlockOnIdentityChange = NO; + [preferences save]; + + NSData *newKey = [SecurityUtils generateRandomBytes:32]; + NSString *recipientId = @"test@gmail.com"; + + [[TSStorageManager sharedManager] saveRemoteIdentity:newKey recipientId:recipientId]; + + XCTAssert([[TSStorageManager sharedManager] isTrustedIdentityKey:newKey recipientId:recipientId]); + + NSData *otherKey = [SecurityUtils generateRandomBytes:32]; + + XCTAssertTrue([[TSStorageManager sharedManager] isTrustedIdentityKey:otherKey recipientId:recipientId]); +} + - (void)testIdentityKey { [[TSStorageManager sharedManager] generateNewIdentityKey];