Merge branch 'charlesmchen/selfSync'

This commit is contained in:
Matthew Chen 2018-10-10 09:59:26 -04:00
commit 1b94207452
10 changed files with 156 additions and 50 deletions

View file

@ -263,7 +263,7 @@ CHECKOUT OPTIONS:
:commit: ff0b95770520133b83a4bd7b26bc2c90b51abc4d
:git: https://github.com/signalapp/SignalCoreKit.git
SignalMetadataKit:
:commit: b0e664410dd3d709355bfdb9d464ae02644aeb74
:commit: beb10a358db0202228b8d67bcb466d877fefb405
:git: https://github.com/signalapp/SignalMetadataKit
SocketRocket:
:commit: 9f9563a83cd8960503074aa8de72206f83fb7a69

View file

@ -19,7 +19,9 @@ public class OWS111UDAttributesMigration: OWSDatabaseMigration {
// increment a similar constant for each migration.
@objc
class func migrationId() -> String {
return "111"
// NOTE: Changes were made to the service after this migration was initially
// merged, so we need to re-migrate any developer devices.
return "111.1"
}
override public func runUp(completion: @escaping OWSDatabaseMigrationCompletion) {

View file

@ -111,6 +111,12 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
object:nil];
}
#pragma mark - Dependencies
- (TSAccountManager *)tsAccountManager {
return TSAccountManager.sharedInstance;
}
- (AFHTTPSessionManager *)avatarHTTPManager
{
return [OWSSignalService sharedInstance].CDNSessionManager;
@ -491,7 +497,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
{
OWSAssertIsOnMainThread();
NSString *_Nullable localNumber = [TSAccountManager sharedInstance].localNumber;
NSString *_Nullable localNumber = self.tsAccountManager.localNumber;
if (!localNumber) {
return;
}
@ -676,7 +682,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
- (void)logUserProfiles
{
[self.dbConnection asyncReadWithBlock:^(YapDatabaseReadTransaction *transaction) {
OWSLogError(@"logUserProfiles: %zd", [transaction numberOfKeysInCollection:OWSUserProfile.collection]);
OWSLogError(@"logUserProfiles: %ld", (unsigned long) [transaction numberOfKeysInCollection:OWSUserProfile.collection]);
[transaction
enumerateKeysAndObjectsInCollection:OWSUserProfile.collection
usingBlock:^(NSString *_Nonnull key, id _Nonnull object, BOOL *_Nonnull stop) {
@ -730,9 +736,11 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
- (nullable OWSAES256Key *)profileKeyForRecipientId:(NSString *)recipientId
{
OWSAssertDebug(recipientId.length > 0);
OWSUserProfile *userProfile =
[OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection];
// For "local reads", use the local user profile.
OWSUserProfile *userProfile = ([self.tsAccountManager.localNumber isEqualToString:recipientId]
? self.localUserProfile
: [OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection]);
OWSAssertDebug(userProfile);
return userProfile.profileKey;
@ -742,8 +750,10 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
{
OWSAssertDebug(recipientId.length > 0);
OWSUserProfile *userProfile =
[OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection];
// For "local reads", use the local user profile.
OWSUserProfile *userProfile = ([self.tsAccountManager.localNumber isEqualToString:recipientId]
? self.localUserProfile
: [OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection]);
return userProfile.profileName;
}
@ -752,8 +762,10 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
{
OWSAssertDebug(recipientId.length > 0);
OWSUserProfile *userProfile =
[OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection];
// For "local reads", use the local user profile.
OWSUserProfile *userProfile = ([self.tsAccountManager.localNumber isEqualToString:recipientId]
? self.localUserProfile
: [OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection]);
if (userProfile.avatarFileName.length > 0) {
return [self loadProfileAvatarWithFilename:userProfile.avatarFileName];
@ -770,8 +782,10 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
{
OWSAssertDebug(recipientId.length > 0);
OWSUserProfile *userProfile =
[OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection];
// For "local reads", use the local user profile.
OWSUserProfile *userProfile = ([self.tsAccountManager.localNumber isEqualToString:recipientId]
? self.localUserProfile
: [OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection]);
if (userProfile.avatarFileName.length > 0) {
return [self loadProfileDataWithFilename:userProfile.avatarFileName];
@ -862,7 +876,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
// If we're updating the profile that corresponds to our local number,
// update the local profile as well.
NSString *_Nullable localNumber = [TSAccountManager sharedInstance].localNumber;
NSString *_Nullable localNumber = self.tsAccountManager.localNumber;
if (localNumber && [localNumber isEqualToString:userProfile.recipientId]) {
OWSUserProfile *localUserProfile = self.localUserProfile;
OWSAssertDebug(localUserProfile);
@ -919,7 +933,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
// If we're updating the profile that corresponds to our local number,
// update the local profile as well.
NSString *_Nullable localNumber = [TSAccountManager sharedInstance].localNumber;
NSString *_Nullable localNumber = self.tsAccountManager.localNumber;
if (localNumber && [localNumber isEqualToString:recipientId]) {
OWSUserProfile *localUserProfile = self.localUserProfile;
OWSAssertDebug(localUserProfile);

View file

@ -3,6 +3,7 @@
//
#import "SignalRecipient.h"
#import "OWSDevice.h"
#import "TSAccountManager.h"
#import <YapDatabase/YapDatabaseConnection.h>
@ -52,7 +53,7 @@ NS_ASSUME_NONNULL_BEGIN
//
// OWSMessageSender will correct this if it is wrong the next time
// we send a message to this recipient.
_devices = [NSOrderedSet orderedSetWithObject:@(1)];
_devices = [NSOrderedSet orderedSetWithObject:@(OWSDevicePrimaryDeviceId)];
}
return self;
@ -69,7 +70,8 @@ NS_ASSUME_NONNULL_BEGIN
_devices = [NSOrderedSet new];
}
if ([self.uniqueId isEqual:[TSAccountManager localNumber]] && [self.devices containsObject:@(1)]) {
if ([self.uniqueId isEqual:[TSAccountManager localNumber]] &&
[self.devices containsObject:@(OWSDevicePrimaryDeviceId)]) {
OWSFailDebug(@"self as recipient device");
}
@ -89,8 +91,9 @@ NS_ASSUME_NONNULL_BEGIN
- (void)addDevices:(NSSet *)devices
{
OWSAssertDebug(devices.count > 0);
if ([self.uniqueId isEqual:[TSAccountManager localNumber]] && [devices containsObject:@(1)]) {
if ([self.uniqueId isEqual:[TSAccountManager localNumber]] &&
[devices containsObject:@(OWSDevicePrimaryDeviceId)]) {
OWSFailDebug(@"adding self as recipient device");
return;
}

View file

@ -10,12 +10,15 @@
#import "ProfileManagerProtocol.h"
#import "SSKEnvironment.h"
#import "SignalAccount.h"
#import "TSAccountManager.h"
#import "TSAttachment.h"
#import "TSAttachmentStream.h"
#import "TSContactThread.h"
#import <SignalCoreKit/NSDate+OWS.h>
#import <SignalServiceKit/SignalServiceKit-Swift.h>
@import Contacts;
NS_ASSUME_NONNULL_BEGIN
@interface OWSSyncContactsMessage ()
@ -49,6 +52,18 @@ NS_ASSUME_NONNULL_BEGIN
return [super initWithCoder:coder];
}
#pragma mark - Dependencies
- (id<ContactsManagerProtocol>)contactsManager {
return SSKEnvironment.shared.contactsManager;
}
- (TSAccountManager *)tsAccountManager {
return TSAccountManager.sharedInstance;
}
#pragma mark -
- (nullable SSKProtoSyncMessageBuilder *)syncMessageBuilder
{
if (self.attachmentIds.count != 1) {
@ -79,7 +94,22 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable NSData *)buildPlainTextAttachmentDataWithTransaction:(YapDatabaseReadTransaction *)transaction
{
id<ContactsManagerProtocol> contactsManager = SSKEnvironment.shared.contactsManager;
NSMutableArray<SignalAccount *> *signalAccounts = [self.signalAccounts mutableCopy];
NSString *_Nullable localNumber = self.tsAccountManager.localNumber;
OWSAssertDebug(localNumber);
if (localNumber) {
BOOL hasLocalNumber = NO;
for (SignalAccount *signalAccount in signalAccounts) {
hasLocalNumber |= [signalAccount.recipientId isEqualToString:localNumber];
}
if (!hasLocalNumber) {
SignalAccount *signalAccount = [[SignalAccount alloc] initWithRecipientId:localNumber];
// OWSContactsOutputStream requires all signalAccount to have a contact.
signalAccount.contact = [[Contact alloc] initWithSystemContact:[CNContact new]];
[signalAccounts addObject:signalAccount];
}
}
// TODO use temp file stream to avoid loading everything into memory at once
// First though, we need to re-engineer our attachment process to accept streams (encrypting with stream,
@ -89,12 +119,11 @@ NS_ASSUME_NONNULL_BEGIN
OWSContactsOutputStream *contactsOutputStream =
[[OWSContactsOutputStream alloc] initWithOutputStream:dataOutputStream];
for (SignalAccount *signalAccount in self.signalAccounts) {
for (SignalAccount *signalAccount in signalAccounts) {
OWSRecipientIdentity *_Nullable recipientIdentity =
[self.identityManager recipientIdentityForRecipientId:signalAccount.recipientId];
NSData *_Nullable profileKeyData = [self.profileManager profileKeyDataForRecipientId:signalAccount.recipientId];
OWSDisappearingMessagesConfiguration *_Nullable disappearingMessagesConfiguration;
NSString *conversationColorName;
@ -109,11 +138,11 @@ NS_ASSUME_NONNULL_BEGIN
[contactsOutputStream writeSignalAccount:signalAccount
recipientIdentity:recipientIdentity
profileKeyData:profileKeyData
contactsManager:contactsManager
contactsManager:self.contactsManager
conversationColorName:conversationColorName
disappearingMessagesConfiguration:disappearingMessagesConfiguration];
}
[dataOutputStream close];
if (contactsOutputStream.hasError) {

View file

@ -16,6 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly, nullable) NSData *plaintextData;
@property (nonatomic, readonly) NSString *source;
@property (nonatomic, readonly) UInt32 sourceDevice;
@property (nonatomic, readonly) BOOL isUDMessage;
@end

View file

@ -8,6 +8,7 @@
#import "NotificationsProtocol.h"
#import "OWSAnalytics.h"
#import "OWSBlockingManager.h"
#import "OWSDevice.h"
#import "OWSError.h"
#import "OWSIdentityManager.h"
#import "OWSPrimaryStorage+PreKeyStore.h"
@ -46,6 +47,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
@property (nonatomic, nullable) NSData *plaintextData;
@property (nonatomic) NSString *source;
@property (nonatomic) UInt32 sourceDevice;
@property (nonatomic) BOOL isUDMessage;
@end
@ -57,6 +59,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
plaintextData:(nullable NSData *)plaintextData
source:(NSString *)source
sourceDevice:(UInt32)sourceDevice
isUDMessage:(BOOL)isUDMessage
{
OWSAssertDebug(envelopeData);
OWSAssertDebug(source.length > 0);
@ -67,6 +70,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
result.plaintextData = plaintextData;
result.source = source;
result.sourceDevice = sourceDevice;
result.isUDMessage = isUDMessage;
return result;
}
@ -134,6 +138,11 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
return [self.blockingManager.blockedPhoneNumbers containsObject:envelope.source];
}
- (TSAccountManager *)tsAccountManager
{
return TSAccountManager.sharedInstance;
}
#pragma mark - Decryption
- (void)decryptEnvelope:(SSKProtoEnvelope *)envelope
@ -157,11 +166,19 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
});
};
NSString *localRecipientId = self.tsAccountManager.localNumber;
uint32_t localDeviceId = OWSDevicePrimaryDeviceId;
DecryptSuccessBlock successBlock = ^(
OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
// Ensure all blocked messages are discarded.
if ([self isEnvelopeSenderBlocked:envelope]) {
OWSLogInfo(@"ignoring blocked envelope: %@", envelope.source);
OWSLogInfo(@"Ignoring blocked envelope: %@", envelope.source);
return failureBlock();
}
if ([result.source isEqualToString:localRecipientId] && result.sourceDevice == localDeviceId) {
// Self-sent messages should be discarded during the decryption process.
OWSFailDebug(@"Unexpected self-sent sync message.");
return failureBlock();
}
@ -237,7 +254,8 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
[OWSMessageDecryptResult resultWithEnvelopeData:envelopeData
plaintextData:nil
source:envelope.source
sourceDevice:envelope.sourceDevice];
sourceDevice:envelope.sourceDevice
isUDMessage:NO];
successBlock(result, transaction);
}];
// Return to avoid double-acknowledging.
@ -361,11 +379,11 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
// plaintextData may be nil for some envelope types.
NSData *_Nullable plaintextData =
[[cipher decrypt:cipherMessage protocolContext:transaction] removePadding];
OWSMessageDecryptResult *result =
[OWSMessageDecryptResult resultWithEnvelopeData:envelopeData
plaintextData:plaintextData
source:envelope.source
sourceDevice:envelope.sourceDevice];
OWSMessageDecryptResult *result = [OWSMessageDecryptResult resultWithEnvelopeData:envelopeData
plaintextData:plaintextData
source:envelope.source
sourceDevice:envelope.sourceDevice
isUDMessage:NO];
successBlock(result, transaction);
} @catch (NSException *exception) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@ -414,6 +432,9 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
id<SMKCertificateValidator> certificateValidator =
[[SMKCertificateDefaultValidator alloc] initWithTrustRoot:self.udManager.trustRoot];
NSString *localRecipientId = self.tsAccountManager.localNumber;
uint32_t localDeviceId = OWSDevicePrimaryDeviceId;
[self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
@try {
NSError *error;
@ -433,9 +454,17 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
[cipher decryptMessageWithCertificateValidator:certificateValidator
cipherTextData:encryptedData
timestamp:serverTimestamp
localRecipientId:localRecipientId
localDeviceId:localDeviceId
protocolContext:transaction
error:&error];
if (error || !decryptResult) {
if ([error.domain isEqualToString:@"SignalMetadataKit.SMKSecretSessionCipherError"]
&& error.code == SMKSecretSessionCipherErrorSelfSentMessage) {
// Self-sent messages can be safely discarded.
return failureBlock(error);
}
OWSFailDebug(@"Could not decrypt UD message: %@", error);
error = EnsureDecryptError(error, @"Could not decrypt UD message");
return failureBlock(error);
@ -471,7 +500,8 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
OWSMessageDecryptResult *result = [OWSMessageDecryptResult resultWithEnvelopeData:newEnvelopeData
plaintextData:plaintextData
source:source
sourceDevice:(uint32_t)sourceDeviceId];
sourceDevice:(uint32_t)sourceDeviceId
isUDMessage:YES];
successBlock(result, transaction);
} @catch (NSException *exception) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@ -517,7 +547,12 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
return;
} else {
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorCorruptMessage], envelope);
errorMessage = [TSErrorMessage corruptedMessageWithEnvelope:envelope withTransaction:transaction];
if (envelope.source.length > 0) {
errorMessage = [TSErrorMessage corruptedMessageWithEnvelope:envelope withTransaction:transaction];
} else {
// TODO: Find another way to surface undecryptable UD messages to the user.
return;
}
}
OWSAssertDebug(errorMessage);

View file

@ -1324,7 +1324,19 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
OWSLogDebug(
@"built message: %@ plainTextData.length: %lu", [messageSend.message class], (unsigned long)plainText.length);
for (NSNumber *deviceNumber in messageSend.recipient.devices) {
OWSLogDebug(@"recipient.devices: %@", recipient.devices);
[DDLog flushLog];
NSMutableArray<NSNumber *> *deviceIds = [recipient.devices mutableCopy];
OWSAssertDebug(deviceIds);
if (messageSend.isUDSend && messageSend.isLocalNumber) {
OWSAssertDebug(![deviceIds containsObject:@(OWSDevicePrimaryDeviceId)]);
[deviceIds addObject:@(OWSDevicePrimaryDeviceId)];
}
for (NSNumber *deviceId in deviceIds) {
@try {
__block NSDictionary *messageDict;
__block NSException *encryptionException;
@ -1332,7 +1344,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
@try {
messageDict = [self encryptedMessageForMessageSend:messageSend
deviceId:deviceNumber
deviceId:deviceId
plainText:plainText
transaction:transaction];
} @catch (NSException *exception) {
@ -1353,7 +1365,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
} @catch (NSException *exception) {
if ([exception.name isEqualToString:OWSMessageSenderInvalidDeviceException]) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[recipient removeDevicesFromRecipient:[NSSet setWithObject:deviceNumber] transaction:transaction];
[recipient removeDevicesFromRecipient:[NSSet setWithObject:deviceId] transaction:transaction];
}];
} else {
@throw exception;

View file

@ -94,10 +94,17 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
return SSKEnvironment.shared.profileManager
}
private var tsAccountManager: TSAccountManager {
return TSAccountManager.sharedInstance()
}
// MARK: - Recipient state
@objc
public func supportsUnidentifiedDelivery(recipientId: String) -> Bool {
if tsAccountManager.localNumber() == recipientId {
return true
}
return dbConnection.bool(forKey: recipientId, inCollection: kUDRecipientModeCollection, defaultValue: false)
}

View file

@ -13,32 +13,37 @@ NS_ASSUME_NONNULL_BEGIN
@implementation ProtoUtils
#pragma mark - Dependencies
+ (id<ProfileManagerProtocol>)profileManager {
return SSKEnvironment.shared.profileManager;
}
+ (OWSAES256Key *)localProfileKey
{
return self.profileManager.localProfileKey;
}
#pragma mark -
+ (BOOL)shouldMessageHaveLocalProfileKey:(TSThread *)thread recipientId:(NSString *_Nullable)recipientId
{
OWSAssertDebug(thread);
id<ProfileManagerProtocol> profileManager = SSKEnvironment.shared.profileManager;
// For 1:1 threads, we want to include the profile key IFF the
// contact is in the whitelist.
//
// For Group threads, we want to include the profile key IFF the
// recipient OR the group is in the whitelist.
if (recipientId.length > 0 && [profileManager isUserInProfileWhitelist:recipientId]) {
if (recipientId.length > 0 && [self.profileManager isUserInProfileWhitelist:recipientId]) {
return YES;
} else if ([profileManager isThreadInProfileWhitelist:thread]) {
} else if ([self.profileManager isThreadInProfileWhitelist:thread]) {
return YES;
}
return NO;
}
+ (OWSAES256Key *)localProfileKey
{
id<ProfileManagerProtocol> profileManager = SSKEnvironment.shared.profileManager;
return profileManager.localProfileKey;
}
+ (void)addLocalProfileKeyIfNecessary:(TSThread *)thread
recipientId:(NSString *_Nullable)recipientId
dataMessageBuilder:(SSKProtoDataMessageBuilder *)dataMessageBuilder
@ -52,10 +57,9 @@ NS_ASSUME_NONNULL_BEGIN
if (recipientId.length > 0) {
// Once we've shared our profile key with a user (perhaps due to being
// a member of a whitelisted group), make sure they're whitelisted.
id<ProfileManagerProtocol> profileManager = SSKEnvironment.shared.profileManager;
// FIXME PERF avoid this dispatch. It's going to happen for *each* recipient in a group message.
dispatch_async(dispatch_get_main_queue(), ^{
[profileManager addUserToProfileWhitelist:recipientId];
[self.profileManager addUserToProfileWhitelist:recipientId];
});
}
}
@ -81,10 +85,9 @@ NS_ASSUME_NONNULL_BEGIN
// Once we've shared our profile key with a user (perhaps due to being
// a member of a whitelisted group), make sure they're whitelisted.
id<ProfileManagerProtocol> profileManager = SSKEnvironment.shared.profileManager;
// FIXME PERF avoid this dispatch. It's going to happen for *each* recipient in a group message.
dispatch_async(dispatch_get_main_queue(), ^{
[profileManager addUserToProfileWhitelist:recipientId];
[self.profileManager addUserToProfileWhitelist:recipientId];
});
}
}