Merge branch 'charlesmchen/ud8'

pull/1/head
Matthew Chen 6 years ago
commit fbf0c51e76

@ -260,10 +260,10 @@ CHECKOUT OPTIONS:
:commit: 8b8326cd50bc488663a3d3743f1a92b90f4d85b4
:git: https://github.com/signalapp/HKDFKit.git
SignalCoreKit:
:commit: 358ec16833d9b8b6e1410d83fa47c819c533fe91
:commit: e5b6aa3c078d7c2fbc154e5dd806b8e55211697d
:git: https://github.com/signalapp/SignalCoreKit.git
SignalMetadataKit:
:commit: 954cbfa767e130626d2e87cc029769a1977c8edd
:commit: b0e664410dd3d709355bfdb9d464ae02644aeb74
:git: https://github.com/signalapp/SignalMetadataKit
SocketRocket:
:commit: 9f9563a83cd8960503074aa8de72206f83fb7a69

@ -1 +1 @@
Subproject commit 536fb61dc6b3b29db9ffe9210bbff1eefb75bef9
Subproject commit e523ff4816a27bf7b990011df50c162255cfda79

@ -151,7 +151,9 @@ public class MessageFetcherJob: NSObject {
throw ParamParser.ParseError.invalidFormat("sourceDevice")
}
let builder = SSKProtoEnvelope.builder(type: type, source: source, sourceDevice: sourceDevice, timestamp: timestamp)
let builder = SSKProtoEnvelope.builder(type: type, timestamp: timestamp)
builder.setSource(source)
builder.setSourceDevice(sourceDevice)
if let legacyMessage = try params.optionalBase64EncodedData(key: "message") {
builder.setLegacyMessage(legacyMessage)
@ -198,8 +200,16 @@ public class MessageFetcherJob: NSObject {
}
private func acknowledgeDelivery(envelope: SSKProtoEnvelope) {
let source = envelope.source
let request = OWSRequestFactory.acknowledgeMessageDeliveryRequest(withSource: source, timestamp: envelope.timestamp)
let request: TSRequest
if let serverGuid = envelope.serverGuid, envelope.hasServerTimestamp {
request = OWSRequestFactory.acknowledgeMessageDeliveryRequest(withServerGuid: serverGuid, serverTimestamp: envelope.serverTimestamp)
} else if let source = envelope.source {
request = OWSRequestFactory.acknowledgeMessageDeliveryRequest(withSource: source, timestamp: envelope.timestamp)
} else {
owsFailDebug("Cannot ACK message which has neither source, nor server GUID and timestamp.")
return
}
self.networkManager.makeRequest(request,
success: { (_: URLSessionDataTask?, _: Any?) -> Void in
Logger.debug("acknowledged delivery for message at timestamp: \(envelope.timestamp)")

@ -1123,7 +1123,7 @@ typedef enum : NSUInteger {
{
[super viewDidAppear:animated];
[ProfileFetcherJob runWithThread:self.thread networkManager:self.networkManager];
[ProfileFetcherJob runWithThread:self.thread];
[self markVisibleMessagesAsRead];
[self startReadTimer];
[self updateNavigationBarSubtitleLabel];

@ -3380,9 +3380,9 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac
}();
SSKProtoEnvelopeBuilder *envelopeBuilder = [SSKProtoEnvelope builderWithType:SSKProtoEnvelopeTypeCiphertext
source:source
sourceDevice:1
timestamp:timestamp];
[envelopeBuilder setSource:source];
[envelopeBuilder setSourceDevice:1];
NSError *error;
SSKProtoEnvelope *_Nullable envelope = [envelopeBuilder buildAndReturnError:&error];
if (error || !envelope) {
@ -3901,8 +3901,9 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac
SSKProtoEnvelopeType envelopeType = SSKProtoEnvelopeTypeCiphertext;
NSData *content = plaintextData;
SSKProtoEnvelopeBuilder *envelopeBuilder =
[SSKProtoEnvelope builderWithType:envelopeType source:source sourceDevice:sourceDevice timestamp:timestamp];
SSKProtoEnvelopeBuilder *envelopeBuilder = [SSKProtoEnvelope builderWithType:envelopeType timestamp:timestamp];
[envelopeBuilder setSource:source];
[envelopeBuilder setSourceDevice:sourceDevice];
envelopeBuilder.content = content;
NSError *error;
NSData *_Nullable envelopeData = [envelopeBuilder buildSerializedDataAndReturnError:&error];

@ -692,8 +692,8 @@ typedef void (^OrphanDataBlock)(OWSOrphanData *);
NSError *error;
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error];
if (!attributes || error) {
OWSLogDebug(@"Could not get attributes of file at: %@", filePath);
OWSFailDebug(@"Could not get attributes of file");
// This is fine; the file may have been deleted since we found it.
OWSLogWarn(@"Could not get attributes of file at: %@", filePath);
continue;
}
// Don't delete files which were created in the last N minutes.

@ -88,9 +88,7 @@ public class OWS106EnsureProfileComplete: OWSDatabaseMigration {
let (promise, fulfill, reject) = Promise<Void>.pending()
let networkManager = SSKEnvironment.shared.networkManager
ProfileFetcherJob(networkManager: networkManager).getProfile(recipientId: localRecipientId).then { _ -> Void in
ProfileFetcherJob().getProfile(recipientId: localRecipientId).then { _ -> Void in
Logger.info("verified recipient profile is in good shape: \(localRecipientId)")
fulfill(())

@ -495,7 +495,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
if (!localNumber) {
return;
}
[ProfileFetcherJob runWithRecipientId:localNumber networkManager:self.networkManager ignoreThrottling:YES];
[ProfileFetcherJob runWithRecipientId:localNumber ignoreThrottling:YES];
}
#pragma mark - Profile Whitelist
@ -716,7 +716,6 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
completion:^{
dispatch_async(dispatch_get_main_queue(), ^(void) {
[ProfileFetcherJob runWithRecipientId:recipientId
networkManager:self.networkManager
ignoreThrottling:YES];
});
}];

@ -9,10 +9,6 @@ import SignalServiceKit
@objc
public class ProfileFetcherJob: NSObject {
let networkManager: TSNetworkManager
let socketManager: TSSocketManager
let primaryStorage: OWSPrimaryStorage
// This property is only accessed on the main queue.
static var fetchDateMap = [String: Date]()
@ -21,22 +17,39 @@ public class ProfileFetcherJob: NSObject {
var backgroundTask: OWSBackgroundTask?
@objc
public class func run(thread: TSThread, networkManager: TSNetworkManager) {
ProfileFetcherJob(networkManager: networkManager).run(recipientIds: thread.recipientIdentifiers)
public class func run(thread: TSThread) {
ProfileFetcherJob().run(recipientIds: thread.recipientIdentifiers)
}
@objc
public class func run(recipientId: String, networkManager: TSNetworkManager, ignoreThrottling: Bool) {
ProfileFetcherJob(networkManager: networkManager, ignoreThrottling: ignoreThrottling).run(recipientIds: [recipientId])
public class func run(recipientId: String, ignoreThrottling: Bool) {
ProfileFetcherJob(ignoreThrottling: ignoreThrottling).run(recipientIds: [recipientId])
}
public init(networkManager: TSNetworkManager, ignoreThrottling: Bool = false) {
self.networkManager = networkManager
self.socketManager = TSSocketManager.shared()
self.primaryStorage = OWSPrimaryStorage.shared()
public init(ignoreThrottling: Bool = false) {
self.ignoreThrottling = ignoreThrottling
}
// MARK: - Dependencies
private var networkManager: TSNetworkManager {
return SSKEnvironment.shared.networkManager
}
private var socketManager: TSSocketManager {
return TSSocketManager.shared()
}
private var primaryStorage: OWSPrimaryStorage {
return SSKEnvironment.shared.primaryStorage
}
private var udManager: OWSUDManager {
return SSKEnvironment.shared.udManager
}
// MARK: -
public func run(recipientIds: [String]) {
AssertIsOnMainThread()
@ -117,7 +130,7 @@ public class ProfileFetcherJob: NSObject {
self.socketManager.make(request,
success: { (responseObject: Any?) -> Void in
do {
let profile = try SignalServiceProfile(recipientId: recipientId, rawResponse: responseObject)
let profile = try SignalServiceProfile(recipientId: recipientId, responseObject: responseObject)
fulfill(profile)
} catch {
reject(error)
@ -130,7 +143,7 @@ public class ProfileFetcherJob: NSObject {
self.networkManager.makeRequest(request,
success: { (_: URLSessionDataTask?, responseObject: Any?) -> Void in
do {
let profile = try SignalServiceProfile(recipientId: recipientId, rawResponse: responseObject)
let profile = try SignalServiceProfile(recipientId: recipientId, responseObject: responseObject)
fulfill(profile)
} catch {
reject(error)
@ -155,6 +168,13 @@ public class ProfileFetcherJob: NSObject {
OWSProfileManager.shared().updateProfile(forRecipientId: signalServiceProfile.recipientId,
profileNameEncrypted: signalServiceProfile.profileNameEncrypted,
avatarUrlPath: signalServiceProfile.avatarUrlPath)
// TODO: We may want to only call setSupportsUnidentifiedDelivery if
// supportsUnidentifiedDelivery is true.
let supportsUnidentifiedDelivery = signalServiceProfile.unidentifiedAccessKey != nil
udManager.setSupportsUnidentifiedDelivery(supportsUnidentifiedDelivery, recipientId: signalServiceProfile.recipientId)
udManager.setShouldAllowUnrestrictedAccess(recipientId: signalServiceProfile.recipientId, shouldAllowUnrestrictedAccess: signalServiceProfile.hasUnrestrictedUnidentifiedAccess)
}
private func verifyIdentityUpToDateAsync(recipientId: String, latestIdentityKey: Data) {
@ -182,37 +202,33 @@ public class SignalServiceProfile: NSObject {
public let identityKey: Data
public let profileNameEncrypted: Data?
public let avatarUrlPath: String?
public let unidentifiedAccessKey: Data?
public let hasUnrestrictedUnidentifiedAccess: Bool
init(recipientId: String, rawResponse: Any?) throws {
init(recipientId: String, responseObject: Any?) throws {
self.recipientId = recipientId
guard let responseDict = rawResponse as? [String: Any?] else {
throw ValidationError.invalid(description: "unexpected type: \(String(describing: rawResponse))")
guard let params = ParamParser(responseObject: responseObject) else {
throw ValidationError.invalid(description: "invalid response: \(String(describing: responseObject))")
}
guard let identityKeyString = responseDict["identityKey"] as? String else {
throw ValidationError.invalidIdentityKey(description: "missing identity key: \(String(describing: rawResponse))")
}
guard let identityKeyWithType = Data(base64Encoded: identityKeyString) else {
throw ValidationError.invalidIdentityKey(description: "unable to parse identity key: \(identityKeyString)")
}
let identityKeyWithType = try params.requiredBase64EncodedData(key: "identityKey")
let kIdentityKeyLength = 33
guard identityKeyWithType.count == kIdentityKeyLength else {
throw ValidationError.invalidIdentityKey(description: "malformed key \(identityKeyString) with decoded length: \(identityKeyWithType.count)")
throw ValidationError.invalidIdentityKey(description: "malformed identity key \(identityKeyWithType.hexadecimalString) with decoded length: \(identityKeyWithType.count)")
}
// `removeKeyType` is an objc category method only on NSData, so temporarily cast.
self.identityKey = (identityKeyWithType as NSData).removeKeyType() as Data
if let profileNameString = responseDict["name"] as? String {
guard let data = Data(base64Encoded: profileNameString) else {
throw ValidationError.invalidProfileName(description: "unable to parse profile name: \(profileNameString)")
}
self.profileNameEncrypted = data
} else {
self.profileNameEncrypted = nil
}
self.profileNameEncrypted = try params.optionalBase64EncodedData(key: "name")
self.avatarUrlPath = responseDict["avatar"] as? String
let avatarUrlPath: String? = try params.optional(key: "avatar")
self.avatarUrlPath = avatarUrlPath
// `removeKeyType` is an objc category method only on NSData, so temporarily cast.
self.identityKey = (identityKeyWithType as NSData).removeKeyType() as Data
// TODO: Should this key be "unidentifiedAccessKey" or "unidentifiedAccess"?
// The docs don't agree with the response from staging.
self.unidentifiedAccessKey = try params.optionalBase64EncodedData(key: "unidentifiedAccess")
self.hasUnrestrictedUnidentifiedAccess = try params.optional(key: "unrestrictedUnidentifiedAccess") ?? false
}
}

@ -26,9 +26,7 @@ message Envelope {
// @required
optional Type type = 1;
// @required
optional string source = 2;
// @required
optional uint32 sourceDevice = 7;
optional string relay = 3;
// @required

@ -10,10 +10,20 @@ NS_ASSUME_NONNULL_BEGIN
@class SSKProtoEnvelope;
@class YapDatabaseReadWriteTransaction;
@interface OWSMessageDecryptResult : NSObject
@property (nonatomic, readonly) NSData *envelopeData;
@property (nonatomic, readonly, nullable) NSData *plaintextData;
@property (nonatomic, readonly) NSString *source;
@property (nonatomic, readonly) UInt32 sourceDevice;
@end
#pragma mark -
// Decryption result includes the envelope since the envelope
// may be altered by the decryption process.
typedef void (^DecryptSuccessBlock)(
NSData *envelopeData, NSData *_Nullable plaintextData, YapDatabaseReadWriteTransaction *transaction);
typedef void (^DecryptSuccessBlock)(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction);
typedef void (^DecryptFailureBlock)(void);
@interface OWSMessageDecrypter : OWSMessageHandler

@ -4,6 +4,7 @@
#import "OWSMessageDecrypter.h"
#import "NSData+messagePadding.h"
#import "NSString+SSK.h"
#import "NotificationsProtocol.h"
#import "OWSAnalytics.h"
#import "OWSBlockingManager.h"
@ -20,6 +21,7 @@
#import "TSErrorMessage.h"
#import "TSPreKeyManager.h"
#import <AxolotlKit/AxolotlExceptions.h>
#import <AxolotlKit/NSData+keyVersionByte.h>
#import <AxolotlKit/SessionCipher.h>
#import <SignalCoreKit/NSData+OWS.h>
#import <SignalCoreKit/Randomness.h>
@ -36,6 +38,42 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
return OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptUDMessage, fallbackErrorDescription);
}
#pragma mark -
@interface OWSMessageDecryptResult ()
@property (nonatomic) NSData *envelopeData;
@property (nonatomic, nullable) NSData *plaintextData;
@property (nonatomic) NSString *source;
@property (nonatomic) UInt32 sourceDevice;
@end
#pragma mark -
@implementation OWSMessageDecryptResult
+ (OWSMessageDecryptResult *)resultWithEnvelopeData:(NSData *)envelopeData
plaintextData:(nullable NSData *)plaintextData
source:(NSString *)source
sourceDevice:(UInt32)sourceDevice
{
OWSAssertDebug(envelopeData);
OWSAssertDebug(source.length > 0);
OWSAssertDebug(sourceDevice > 0);
OWSMessageDecryptResult *result = [OWSMessageDecryptResult new];
result.envelopeData = envelopeData;
result.plaintextData = plaintextData;
result.source = source;
result.sourceDevice = sourceDevice;
return result;
}
@end
#pragma mark -
@interface OWSMessageDecrypter ()
@property (nonatomic, readonly) OWSPrimaryStorage *primaryStorage;
@ -64,7 +102,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
return self;
}
#pragma mark - Singletons
#pragma mark - Dependencies
- (OWSBlockingManager *)blockingManager
{
@ -80,6 +118,13 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
return SSKEnvironment.shared.identityManager;
}
- (id<OWSUDManager>)udManager
{
OWSAssertDebug(SSKEnvironment.shared.udManager);
return SSKEnvironment.shared.udManager;
}
#pragma mark - Blocking
- (BOOL)isEnvelopeSenderBlocked:(SSKProtoEnvelope *)envelope
@ -92,12 +137,12 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
#pragma mark - Decryption
- (void)decryptEnvelope:(SSKProtoEnvelope *)envelope
envelopeData:(NSData *)envelopeDataOriginal
envelopeData:(NSData *)envelopeData
successBlock:(DecryptSuccessBlock)successBlockParameter
failureBlock:(DecryptFailureBlock)failureBlockParameter
{
OWSAssertDebug(envelope);
OWSAssertDebug(envelopeDataOriginal);
OWSAssertDebug(envelopeData);
OWSAssertDebug(successBlockParameter);
OWSAssertDebug(failureBlockParameter);
OWSAssertDebug([TSAccountManager isRegistered]);
@ -112,36 +157,48 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
});
};
DecryptSuccessBlock successBlock
= ^(NSData *envelopeDataFinal, NSData *_Nullable plaintextData, YapDatabaseReadWriteTransaction *transaction) {
// Having received a valid (decryptable) message from this user,
// make note of the fact that they have a valid Signal account.
[SignalRecipient markRecipientAsRegistered:envelope.source
deviceId:envelope.sourceDevice
transaction:transaction];
DecryptSuccessBlock successBlock = ^(
OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
// Ensure all blocked messages are discarded.
if ([self isEnvelopeSenderBlocked:envelope]) {
OWSLogInfo(@"ignoring blocked envelope: %@", envelope.source);
return failureBlock();
}
successBlockParameter(envelopeDataFinal, plaintextData, transaction);
};
// Having received a valid (decryptable) message from this user,
// make note of the fact that they have a valid Signal account.
[SignalRecipient markRecipientAsRegistered:result.source deviceId:result.sourceDevice transaction:transaction];
successBlockParameter(result, transaction);
};
@try {
OWSLogInfo(@"decrypting envelope: %@", [self descriptionForEnvelope:envelope]);
OWSAssertDebug(envelope.source.length > 0);
if ([self isEnvelopeSenderBlocked:envelope]) {
OWSLogInfo(@"ignoring blocked envelope: %@", envelope.source);
failureBlock();
return;
if (envelope.type != SSKProtoEnvelopeTypeUnidentifiedSender) {
if (!envelope.hasSource || envelope.source.length < 1 || !envelope.source.isValidE164) {
OWSFailDebug(@"incoming envelope has invalid source");
return failureBlock();
}
if (!envelope.hasSourceDevice || envelope.sourceDevice < 1) {
OWSFailDebug(@"incoming envelope has invalid source device");
return failureBlock();
}
// We block UD messages later, after they are decrypted.
if ([self isEnvelopeSenderBlocked:envelope]) {
OWSLogInfo(@"ignoring blocked envelope: %@", envelope.source);
return failureBlock();
}
}
switch (envelope.type) {
case SSKProtoEnvelopeTypeCiphertext: {
[self decryptSecureMessage:envelope
envelopeData:envelopeDataOriginal
successBlock:^(NSData *envelopeDataFinal,
NSData *_Nullable plaintextData,
YapDatabaseReadWriteTransaction *transaction) {
envelopeData:envelopeData
successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
OWSLogDebug(@"decrypted secure message.");
successBlock(envelopeDataFinal, plaintextData, transaction);
successBlock(result, transaction);
}
failureBlock:^(NSError *_Nullable error) {
OWSLogError(@"decrypting secure message from address: %@ failed with error: %@",
@ -155,12 +212,10 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
}
case SSKProtoEnvelopeTypePrekeyBundle: {
[self decryptPreKeyBundle:envelope
envelopeData:envelopeDataOriginal
successBlock:^(NSData *envelopeDataFinal,
NSData *_Nullable plaintextData,
YapDatabaseReadWriteTransaction *transaction) {
envelopeData:envelopeData
successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
OWSLogDebug(@"decrypted pre-key whisper message");
successBlock(envelopeDataFinal, plaintextData, transaction);
successBlock(result, transaction);
}
failureBlock:^(NSError *_Nullable error) {
OWSLogError(@"decrypting pre-key whisper message from address: %@ failed "
@ -178,18 +233,21 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
case SSKProtoEnvelopeTypeKeyExchange:
case SSKProtoEnvelopeTypeUnknown: {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
successBlock(envelopeDataOriginal, nil, transaction);
OWSMessageDecryptResult *result =
[OWSMessageDecryptResult resultWithEnvelopeData:envelopeData
plaintextData:nil
source:envelope.source
sourceDevice:envelope.sourceDevice];
successBlock(result, transaction);
}];
// Return to avoid double-acknowledging.
return;
}
case SSKProtoEnvelopeTypeUnidentifiedSender: {
[self decryptUnidentifiedSender:envelope
successBlock:^(NSData *envelopeDataFinal,
NSData *_Nullable plaintextData,
YapDatabaseReadWriteTransaction *transaction) {
successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
OWSLogDebug(@"decrypted unidentified sender message");
successBlock(envelopeDataFinal, plaintextData, transaction);
successBlock(result, transaction);
}
failureBlock:^(NSError *_Nullable error) {
OWSLogError(@"decrypting unidentified sender message from address: %@ failed "
@ -303,13 +361,18 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
// plaintextData may be nil for some envelope types.
NSData *_Nullable plaintextData =
[[cipher decrypt:cipherMessage protocolContext:transaction] removePadding];
successBlock(envelopeData, plaintextData, transaction);
OWSMessageDecryptResult *result =
[OWSMessageDecryptResult resultWithEnvelopeData:envelopeData
plaintextData:plaintextData
source:envelope.source
sourceDevice:envelope.sourceDevice];
successBlock(result, transaction);
} @catch (NSException *exception) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self processException:exception envelope:envelope];
NSString *errorDescription = [NSString
stringWithFormat:@"Exception while decrypting %@: %@", cipherTypeName, exception.description];
OWSFailDebug(@"%@", errorDescription);
OWSLogError(@"%@", errorDescription);
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorDescription);
failureBlock(error);
});
@ -339,14 +402,17 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
if (!envelope.hasServerTimestamp) {
NSString *errorDescription = @"UD Envelope is missing server timestamp.";
OWSFailDebug(@"%@", errorDescription);
// TODO: We're seeing incoming UD envelopes without a server timestamp on staging.
// Until this is fixed, disabling this assert.
// OWSFailDebug(@"%@", errorDescription);
OWSLogError(@"%@", errorDescription);
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptUDMessage, errorDescription);
return failureBlock(error);
}
UInt64 serverTimestamp = envelope.serverTimestamp;
id<SMKCertificateValidator> certificateValidator =
[[SMKCertificateDefaultValidator alloc] initWithTrustRoot:self.trustRoot];
[[SMKCertificateDefaultValidator alloc] initWithTrustRoot:self.udManager.trustRoot];
[self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
@try {
@ -376,12 +442,13 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
}
NSString *source = decryptResult.senderRecipientId;
if (source.length < 1) {
if (source.length < 1 || !source.isValidE164) {
NSString *errorDescription = @"Invalid UD sender.";
OWSFailDebug(@"%@", errorDescription);
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptUDMessage, errorDescription);
return failureBlock(error);
}
long sourceDeviceId = decryptResult.senderDeviceId;
if (sourceDeviceId < 1 || sourceDeviceId > UINT32_MAX) {
NSString *errorDescription = @"Invalid UD sender device id.";
@ -401,13 +468,17 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
return failureBlock(error);
}
successBlock(newEnvelopeData, plaintextData, transaction);
OWSMessageDecryptResult *result = [OWSMessageDecryptResult resultWithEnvelopeData:newEnvelopeData
plaintextData:plaintextData
source:source
sourceDevice:(uint32_t)sourceDeviceId];
successBlock(result, transaction);
} @catch (NSException *exception) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self processException:exception envelope:envelope];
NSString *errorDescription =
[NSString stringWithFormat:@"Exception while decrypting ud message: %@", exception.description];
OWSFailDebug(@"%@", errorDescription);
OWSLogError(@"%@", errorDescription);
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorDescription);
failureBlock(error);
});
@ -415,19 +486,6 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
}];
}
- (ECPublicKey *)trustRoot
{
NSData *_Nullable trustRootData = [NSData dataFromBase64String:kUDTrustRoot];
OWSAssert(trustRootData);
NSError *error;
ECPublicKey *_Nullable trustRoot = [[ECPublicKey alloc] initWithKeyData:trustRootData error:&error];
if (error || !trustRoot) {
// This exits.
OWSFail(@"Invalid UD trust root.");
}
return trustRoot;
}
- (void)processException:(NSException *)exception envelope:(SSKProtoEnvelope *)envelope
{
OWSLogError(

@ -34,6 +34,8 @@ NSString *envelopeAddress(SSKProtoEnvelope *envelope)
return @"KeyExchange";
case SSKProtoEnvelopeTypePrekeyBundle:
return @"PreKeyEncryptedMessage";
case SSKProtoEnvelopeTypeUnidentifiedSender:
return @"UnidentifiedSender";
default:
// Shouldn't happen
OWSProdFail([OWSAnalyticsEvents messageManagerErrorEnvelopeTypeOther]);

@ -208,17 +208,21 @@ NS_ASSUME_NONNULL_BEGIN
OWSLogInfo(@"handling decrypted envelope: %@", [self descriptionForEnvelope:envelope]);
if (!envelope.source.isValidE164) {
if (!envelope.hasSource || envelope.source.length < 1 || !envelope.source.isValidE164) {
OWSFailDebug(@"incoming envelope has invalid source");
return;
}
if (!envelope.hasSourceDevice || envelope.sourceDevice < 1) {
OWSFailDebug(@"incoming envelope has invalid source device");
return;
}
OWSAssertDebug(envelope.source.length > 0);
OWSAssertDebug(![self isEnvelopeSenderBlocked:envelope]);
switch (envelope.type) {
case SSKProtoEnvelopeTypeCiphertext:
case SSKProtoEnvelopeTypePrekeyBundle:
case SSKProtoEnvelopeTypeUnidentifiedSender:
if (!plaintextData) {
OWSFailDebug(@"missing decrypted data for envelope: %@", [self descriptionForEnvelope:envelope]);
return;

@ -72,7 +72,7 @@ NS_ASSUME_NONNULL_BEGIN
NSError *error;
SSKProtoEnvelope *_Nullable envelope = [SSKProtoEnvelope parseData:self.envelopeData error:&error];
if (error || envelope == nil) {
OWSFailDebug(@"failed to parase envelope with error: %@", error);
OWSFailDebug(@"failed to parse envelope with error: %@", error);
return nil;
}
@ -358,8 +358,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
[self.messageDecrypter decryptEnvelope:envelope
envelopeData:job.envelopeData
successBlock:^(
NSData *envelopeData, NSData *_Nullable plaintextData, YapDatabaseReadWriteTransaction *transaction) {
successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) {
OWSAssertDebug(transaction);
// We persist the decrypted envelope data in the same transaction within which
@ -368,8 +367,8 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin
//
// NOTE: We use envelopeData from the decrypt result, not job.envelopeData,
// since the envelope may be altered by the decryption process in the UD case.
[self.batchMessageProcessor enqueueEnvelopeData:envelopeData
plaintextData:plaintextData
[self.batchMessageProcessor enqueueEnvelopeData:result.envelopeData
plaintextData:result.plaintextData
transaction:transaction];
dispatch_async(self.serialQueue, ^{

@ -0,0 +1,79 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
import SignalMetadataKit
// Corresponds to a single effort to send a message to a given recipient,
// which may span multiple attempts. Note that group messages may be sent
// to multiple recipients and therefore require multiple instances of
// OWSMessageSend.
@objc
public class OWSMessageSend: NSObject {
@objc
public let message: TSOutgoingMessage
// thread may be nil if message is an OWSOutgoingSyncMessage.
@objc
public let thread: TSThread?
@objc
public let recipient: SignalRecipient
private static let kMaxRetriesPerRecipient: Int = 3
@objc
public var remainingAttempts = OWSMessageSend.kMaxRetriesPerRecipient
// We "fail over" to REST sends after _any_ error sending
// via the web socket.
@objc
public var hasWebsocketSendFailed = false
// We "fail over" to non-UD sends after auth errors sending via UD.
@objc
public var hasUDAuthFailed = false
@objc
public let udAccessKey: SMKUDAccessKey?
@objc
public let localNumber: String
@objc
public let isLocalNumber: Bool
@objc
public let senderCertificate: SMKSenderCertificate?
@objc
public init(message: TSOutgoingMessage,
thread: TSThread?,
recipient: SignalRecipient,
senderCertificate: SMKSenderCertificate?,
udManager: OWSUDManager,
localNumber: String) {
self.message = message
self.thread = thread
self.recipient = recipient
self.senderCertificate = senderCertificate
var udAccessKey: SMKUDAccessKey?
var isLocalNumber: Bool = false
if let recipientId = recipient.uniqueId {
udAccessKey = udManager.udAccessKeyForRecipient(recipientId)
isLocalNumber = localNumber == recipientId
} else {
owsFailDebug("SignalRecipient missing recipientId")
}
self.udAccessKey = udAccessKey
self.localNumber = localNumber
self.isLocalNumber = isLocalNumber
}
@objc
public var isUDSend: Bool {
return (!hasUDAuthFailed && udAccessKey != nil && senderCertificate != nil)
}
}

@ -39,6 +39,7 @@
#import "TSOutgoingMessage.h"
#import "TSPreKeyManager.h"
#import "TSQuotedMessage.h"
#import "TSRequest.h"
#import "TSSocketManager.h"
#import "TSThread.h"
#import <AxolotlKit/AxolotlExceptions.h>
@ -47,8 +48,10 @@
#import <AxolotlKit/SessionBuilder.h>
#import <AxolotlKit/SessionCipher.h>
#import <PromiseKit/AnyPromise.h>
#import <SignalCoreKit/NSData+OWS.h>
#import <SignalCoreKit/NSDate+OWS.h>
#import <SignalCoreKit/Threading.h>
#import <SignalMetadataKit/SignalMetadataKit-Swift.h>
#import <SignalServiceKit/SignalServiceKit-Swift.h>
NS_ASSUME_NONNULL_BEGIN
@ -195,7 +198,8 @@ void AssertIsOnSendingQueue()
@end
int const OWSMessageSenderRetryAttempts = 3;
#pragma mark -
NSString *const OWSMessageSenderInvalidDeviceException = @"InvalidDeviceException";
NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
@ -207,6 +211,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
@end
#pragma mark -
@implementation OWSMessageSender
- (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage
@ -225,6 +231,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
return self;
}
#pragma mark - Dependencies
- (id<ContactsManagerProtocol>)contactsManager
{
OWSAssertDebug(SSKEnvironment.shared.contactsManager);
@ -246,6 +254,25 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
return SSKEnvironment.shared.networkManager;
}
- (id<OWSUDManager>)udManager
{
OWSAssertDebug(SSKEnvironment.shared.udManager);
return SSKEnvironment.shared.udManager;
}
- (TSAccountManager *)tsAccountManager
{
return TSAccountManager.sharedInstance;
}
- (OWSIdentityManager *)identityManager
{
return SSKEnvironment.shared.identityManager;
}
#pragma mark -
- (NSOperationQueue *)sendingQueueForMessage:(TSOutgoingMessage *)message
{
OWSAssertDebug(message);
@ -435,31 +462,35 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
});
}
- (NSArray<SignalRecipient *> *)signalRecipientsForRecipientIds:(NSArray<NSString *> *)recipientIds
message:(TSOutgoingMessage *)message
- (void)sendMessageToService:(TSOutgoingMessage *)message
success:(void (^)(void))success
failure:(RetryableFailureHandler)failure
{
OWSAssertDebug(recipientIds);
OWSAssertDebug(message);
if (message.thread && message.thread.isGroupThread) {
[self saveInfoMessageForGroupMessage:message inThread:message.thread];
}
NSMutableArray<SignalRecipient *> *recipients = [NSMutableArray new];
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
for (NSString *recipientId in recipientIds) {
SignalRecipient *recipient =
[SignalRecipient getOrBuildUnsavedRecipientForRecipientId:recipientId transaction:transaction];
[recipients addObject:recipient];
[self.udManager
ensureSenderCertificateObjCWithSuccess:^(SMKSenderCertificate *senderCertificate) {
[self sendMessageToService:message senderCertificate:senderCertificate success:success failure:failure];
}
}];
return recipients;
failure:^(NSError *error) {
OWSLogError(@"Could not obtain UD sender certificate: %@", error);
// Proceed using non-UD message sends.
[self sendMessageToService:message senderCertificate:nil success:success failure:failure];
}];
}
- (void)sendMessageToService:(TSOutgoingMessage *)message
senderCertificate:(nullable SMKSenderCertificate *)senderCertificate
success:(void (^)(void))successHandler
failure:(RetryableFailureHandler)failureHandler
{
dispatch_async([OWSDispatch sendingQueue], ^{
TSThread *_Nullable thread = message.thread;
// TODO: It would be nice to combine the "contact" and "group" send logic here.
// In the "self-send" special case, we ony need to send a sync message with a delivery receipt.
if ([thread isKindOfClass:[TSContactThread class]] &&
[((TSContactThread *)thread).contactIdentifier isEqualToString:[TSAccountManager localNumber]]) {
// Send to self.
@ -472,12 +503,14 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}
}];
[self handleMessageSentLocally:message];
[self handleMessageSentLocally:message senderCertificate:senderCertificate];
successHandler();
return;
} else if ([thread isKindOfClass:[TSGroupThread class]]) {
}
NSMutableSet<NSString *> *recipientIds = [NSMutableSet new];
if (thread.isGroupThread) {
TSGroupThread *gThread = (TSGroupThread *)thread;
// Send to the intersection of:
@ -491,48 +524,19 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
// * The recipient is still in the group.
// * The recipient is in the "sending" state.
NSMutableSet<NSString *> *sendingRecipientIds = [NSMutableSet setWithArray:message.sendingRecipientIds];
[sendingRecipientIds intersectSet:[NSSet setWithArray:gThread.groupModel.groupMemberIds]];
[sendingRecipientIds minusSet:[NSSet setWithArray:self.blockingManager.blockedPhoneNumbers]];
[recipientIds addObjectsFromArray:message.sendingRecipientIds];
// Only send to members in the latest known group member list.
[recipientIds intersectSet:[NSSet setWithArray:gThread.groupModel.groupMemberIds]];
// Mark skipped recipients as such. We skip because:
//
// * Recipient is no longer in the group.
// * Recipient is blocked.
//
// Elsewhere, we skip recipient if their Signal account has been deactivated.
NSMutableSet<NSString *> *obsoleteRecipientIds = [NSMutableSet setWithArray:message.sendingRecipientIds];
[obsoleteRecipientIds minusSet:sendingRecipientIds];
if (obsoleteRecipientIds.count > 0) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (NSString *recipientId in obsoleteRecipientIds) {
// Mark this recipient as "skipped".
[message updateWithSkippedRecipient:recipientId transaction:transaction];
}
}];
if ([recipientIds containsObject:TSAccountManager.localNumber]) {
OWSFailDebug(@"Message send recipients should not include self.");
}
} else if ([message isKindOfClass:[OWSOutgoingSyncMessage class]]) {
[recipientIds addObject:[TSAccountManager localNumber]];
} else if ([thread isKindOfClass:[TSContactThread class]]) {
NSString *recipientContactId = ((TSContactThread *)thread).contactIdentifier;
if (sendingRecipientIds.count < 1) {
// All recipients are already sent or can be skipped.
successHandler();
return;
}
NSArray<SignalRecipient *> *recipients =
[self signalRecipientsForRecipientIds:sendingRecipientIds.allObjects message:message];
OWSAssertDebug(recipients.count == sendingRecipientIds.count);
[self groupSend:recipients message:message thread:gThread success:successHandler failure:failureHandler];
} else if ([thread isKindOfClass:[TSContactThread class]]
|| [message isKindOfClass:[OWSOutgoingSyncMessage class]]) {
TSContactThread *contactThread = (TSContactThread *)thread;
NSString *recipientContactId
= ([message isKindOfClass:[OWSOutgoingSyncMessage class]] ? [TSAccountManager localNumber]
: contactThread.contactIdentifier);
// Treat 1:1 sends to blocked contacts as failures.
// If we block a user, don't send 1:1 messages to them. The UI
// should prevent this from occurring, but in some edge cases
// you might, for example, have a pending outgoing message when
@ -547,28 +551,11 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
return;
}
NSArray<SignalRecipient *> *recipients =
[self signalRecipientsForRecipientIds:@[recipientContactId] message:message];
OWSAssertDebug(recipients.count == 1);
SignalRecipient *recipient = recipients.firstObject;
[recipientIds addObject:recipientContactId];
if (!recipient) {
NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
OWSLogWarn(@"recipient contact still not found after attempting lookup.");
// No need to repeat trying to find a failure. Apart from repeatedly failing, it would also cause us to
// print redundant error messages.
[error setIsRetryable:NO];
failureHandler(error);
return;
if ([recipientIds containsObject:TSAccountManager.localNumber]) {
OWSFailDebug(@"Message send recipients should not include self.");
}
[self sendMessageToService:message
recipient:recipient
thread:thread
attempts:OWSMessageSenderRetryAttempts
useWebsocketIfAvailable:YES
success:successHandler
failure:failureHandler];
} else {
// Neither a group nor contact thread? This should never happen.
OWSFailDebug(@"Unknown message type: %@", [message class]);
@ -576,38 +563,74 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
[error setIsRetryable:NO];
failureHandler(error);
return;
}
[recipientIds minusSet:[NSSet setWithArray:self.blockingManager.blockedPhoneNumbers]];
// Mark skipped recipients as such. We skip because:
//
// * Recipient is no longer in the group.
// * Recipient is blocked.
//
// Elsewhere, we skip recipient if their Signal account has been deactivated.
NSMutableSet<NSString *> *obsoleteRecipientIds = [NSMutableSet setWithArray:message.sendingRecipientIds];
[obsoleteRecipientIds minusSet:recipientIds];
if (obsoleteRecipientIds.count > 0) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (NSString *recipientId in obsoleteRecipientIds) {
// Mark this recipient as "skipped".
[message updateWithSkippedRecipient:recipientId transaction:transaction];
}
}];
}
if (recipientIds.count < 1) {
// All recipients are already sent or can be skipped.
successHandler();
return;
}
NSMutableArray<OWSMessageSend *> *messageSends = [NSMutableArray new];
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
for (NSString *recipientId in recipientIds) {
SignalRecipient *recipient =
[SignalRecipient getOrBuildUnsavedRecipientForRecipientId:recipientId transaction:transaction];
OWSMessageSend *messageSend =
[[OWSMessageSend alloc] initWithMessage:message
thread:thread
recipient:recipient
senderCertificate:senderCertificate
udManager:self.udManager
localNumber:self.tsAccountManager.localNumber];
[messageSends addObject:messageSend];
}
}];
OWSAssertDebug(messageSends.count == recipientIds.count);
OWSAssertDebug(messageSends.count > 0);
[self sendWithMessageSends:messageSends
isGroupSend:thread.isGroupThread
success:successHandler
failure:failureHandler];
});
}
- (void)groupSend:(NSArray<SignalRecipient *> *)recipients
message:(TSOutgoingMessage *)message
thread:(TSThread *)thread
success:(void (^)(void))successHandler
failure:(RetryableFailureHandler)failureHandler
- (void)sendWithMessageSends:(NSArray<OWSMessageSend *> *)messageSends
isGroupSend:(BOOL)isGroupSend
success:(void (^)(void))successHandler
failure:(RetryableFailureHandler)failureHandler
{
[self saveGroupMessage:message inThread:thread];
OWSAssertDebug(messageSends.count > 0);
AssertIsOnSendingQueue();
NSMutableArray<AnyPromise *> *sendPromises = [NSMutableArray array];
NSMutableArray<NSError *> *sendErrors = [NSMutableArray array];
for (SignalRecipient *recipient in recipients) {
NSString *recipientId = recipient.recipientId;
// We don't need to send the message to ourselves...
if ([recipientId isEqualToString:[TSAccountManager localNumber]]) {
continue;
}
// ...otherwise we send.
for (OWSMessageSend *messageSend in messageSends) {
// For group sends, we're using chained promises to make the code more readable.
AnyPromise *sendPromise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
[self sendMessageToService:message
recipient:recipient
thread:thread
attempts:OWSMessageSenderRetryAttempts
useWebsocketIfAvailable:YES
[self sendMessageToRecipient:messageSend
success:^{
// The value doesn't matter, we just need any non-NSError value.
resolve(@(1));
@ -643,7 +666,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
// Some errors should be ignored when sending messages
// to groups. See discussion on
// NSError (OWSMessageSender) category.
if ([error shouldBeIgnoredForGroups]) {
if (isGroupSend && [error shouldBeIgnoredForGroups]) {
continue;
}
@ -664,7 +687,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}
}
// If any of the group send errors are retryable, we want to retry.
// If any of the send errors are retryable, we want to retry.
// Therefore, prefer to propagate a retryable error.
if (firstRetryableError) {
return failureHandler(firstRetryableError);
@ -674,7 +697,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
// If we only received errors that we should ignore,
// consider this send a success, unless the message could
// not be sent to any recipient.
if (message.sentRecipientsCount == 0) {
if (messageSends.lastObject.message.sentRecipientsCount == 0) {
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeMessageSendNoValidRecipients,
NSLocalizedString(@"ERROR_DESCRIPTION_NO_VALID_RECIPIENTS",
@"Error indicating that an outgoing message had no valid recipients."));
@ -713,60 +736,19 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}];
}
- (void)sendMessageToService:(TSOutgoingMessage *)message
recipient:(SignalRecipient *)recipient
thread:(nullable TSThread *)thread
attempts:(int)remainingAttemptsParam
useWebsocketIfAvailable:(BOOL)useWebsocketIfAvailable
success:(void (^)(void))successHandler
failure:(RetryableFailureHandler)failureHandler
- (nullable NSArray<NSDictionary *> *)deviceMessagesForMessageSendSafe:(OWSMessageSend *)messageSend
error:(NSError **)errorHandle
{
OWSAssertDebug(message);
OWSAssertDebug(recipient);
OWSAssertDebug(thread || [message isKindOfClass:[OWSOutgoingSyncMessage class]]);
OWSLogInfo(@"attempting to send message: %@, timestamp: %llu, recipient: %@",
message.class,
message.timestamp,
recipient.uniqueId);
OWSAssertDebug(messageSend);
OWSAssertDebug(errorHandle);
AssertIsOnSendingQueue();
if ([TSPreKeyManager isAppLockedDueToPreKeyUpdateFailures]) {
OWSProdError([OWSAnalyticsEvents messageSendErrorFailedDueToPrekeyUpdateFailures]);
// Retry prekey update every time user tries to send a message while app
// is disabled due to prekey update failures.
//
// Only try to update the signed prekey; updating it is sufficient to
// re-enable message sending.
[TSPreKeyManager
rotateSignedPreKeyWithSuccess:^{
OWSLogInfo(@"New prekeys registered with server.");
NSError *error = OWSErrorMakeMessageSendDisabledDueToPreKeyUpdateFailuresError();
[error setIsRetryable:YES];
return failureHandler(error);
}
failure:^(NSError *error) {
OWSLogWarn(@"Failed to update prekeys with the server: %@", error);
return failureHandler(error);
}];
}
if (remainingAttemptsParam <= 0) {
// We should always fail with a specific error.
OWSProdFail([OWSAnalyticsEvents messageSenderErrorGenericSendFailure]);
NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
[error setIsRetryable:YES];
return failureHandler(error);
}
int remainingAttempts = remainingAttemptsParam - 1;
SignalRecipient *recipient = messageSend.recipient;
NSArray<NSDictionary *> *deviceMessages;
@try {
deviceMessages = [self deviceMessages:message recipient:recipient];
deviceMessages = [self deviceMessagesForMessageSendUnsafe:messageSend];
} @catch (NSException *exception) {
deviceMessages = @[];
if ([exception.name isEqualToString:UntrustedIdentityKeyException]) {
// This *can* happen under normal usage, but it should happen relatively rarely.
// We expect it to happen whenever Bob reinstalls, and Alice messages Bob before
@ -788,67 +770,128 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
[error setIsRetryable:NO];
// Avoid the "Too many failures with this contact" error rate limiting.
[error setIsFatal:YES];
*errorHandle = error;
PreKeyBundle *_Nullable newKeyBundle = exception.userInfo[TSInvalidPreKeyBundleKey];
if (newKeyBundle == nil) {
OWSProdFail([OWSAnalyticsEvents messageSenderErrorMissingNewPreKeyBundle]);
failureHandler(error);
return;
return nil;
}
if (![newKeyBundle isKindOfClass:[PreKeyBundle class]]) {
OWSProdFail([OWSAnalyticsEvents messageSenderErrorUnexpectedKeyBundle]);
failureHandler(error);
return;
return nil;
}
NSData *newIdentityKeyWithVersion = newKeyBundle.identityKey;
if (![newIdentityKeyWithVersion isKindOfClass:[NSData class]]) {
OWSProdFail([OWSAnalyticsEvents messageSenderErrorInvalidIdentityKeyType]);
failureHandler(error);
return;
return nil;
}
// TODO migrate to storing the full 33 byte representation of the identity key.
if (newIdentityKeyWithVersion.length != kIdentityKeyLength) {
OWSProdFail([OWSAnalyticsEvents messageSenderErrorInvalidIdentityKeyLength]);
failureHandler(error);
return;
return nil;
}
NSData *newIdentityKey = [newIdentityKeyWithVersion removeKeyType];
[[OWSIdentityManager sharedManager] saveRemoteIdentity:newIdentityKey recipientId:recipient.recipientId];
[self.identityManager saveRemoteIdentity:newIdentityKey recipientId:recipient.recipientId];
failureHandler(error);
return;
return nil;
}
if ([exception.name isEqualToString:OWSMessageSenderRateLimitedException]) {
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeSignalServiceRateLimited,
NSLocalizedString(@"FAILED_SENDING_BECAUSE_RATE_LIMIT",
@"action sheet header when re-sending message which failed because of too many attempts"));
// We're already rate-limited. No need to exacerbate the problem.
[error setIsRetryable:NO];
// Avoid exacerbating the rate limiting.
[error setIsFatal:YES];
return failureHandler(error);
*errorHandle = error;
return nil;
}
if (remainingAttempts == 0) {
OWSLogWarn(@"Terminal failure to build any device messages. Giving up with exception:%@", exception);
if (messageSend.remainingAttempts == 0) {
OWSLogWarn(@"Terminal failure to build any device messages. Giving up with exception: %@", exception);
NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
// Since we've already repeatedly failed to build messages, it's unlikely that repeating the whole process
// will succeed.
[error setIsRetryable:NO];
return failureHandler(error);
*errorHandle = error;
return nil;
}
OWSLogWarn(@"Could not build device messages: %@", exception);
NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
[error setIsRetryable:YES];
*errorHandle = error;
return nil;
}
NSString *localNumber = [TSAccountManager localNumber];
BOOL isLocalNumber = [localNumber isEqualToString:recipient.uniqueId];
if (isLocalNumber) {
return deviceMessages;
}
- (void)sendMessageToRecipient:(OWSMessageSend *)messageSend
success:(void (^)(void))successHandler
failure:(RetryableFailureHandler)failureHandler
{
OWSAssertDebug(messageSend);
OWSAssertDebug(messageSend.thread || [messageSend.message isKindOfClass:[OWSOutgoingSyncMessage class]]);
TSOutgoingMessage *message = messageSend.message;
SignalRecipient *recipient = messageSend.recipient;
OWSLogInfo(@"attempting to send message: %@, timestamp: %llu, recipient: %@",
message.class,
message.timestamp,
recipient.uniqueId);
AssertIsOnSendingQueue();
if ([TSPreKeyManager isAppLockedDueToPreKeyUpdateFailures]) {
OWSProdError([OWSAnalyticsEvents messageSendErrorFailedDueToPrekeyUpdateFailures]);
// Retry prekey update every time user tries to send a message while app
// is disabled due to prekey update failures.
//
// Only try to update the signed prekey; updating it is sufficient to
// re-enable message sending.
[TSPreKeyManager
rotateSignedPreKeyWithSuccess:^{
OWSLogInfo(@"New prekeys registered with server.");
NSError *error = OWSErrorMakeMessageSendDisabledDueToPreKeyUpdateFailuresError();
[error setIsRetryable:YES];
return failureHandler(error);
}
failure:^(NSError *error) {
OWSLogWarn(@"Failed to update prekeys with the server: %@", error);
return failureHandler(error);
}];
}
if (messageSend.remainingAttempts <= 0) {
// We should always fail with a specific error.
OWSProdFail([OWSAnalyticsEvents messageSenderErrorGenericSendFailure]);
NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
[error setIsRetryable:YES];
return failureHandler(error);
}
// Consume an attempt.
messageSend.remainingAttempts = messageSend.remainingAttempts - 1;
NSError *deviceMessagesError;
NSArray<NSDictionary *> *_Nullable deviceMessages =
[self deviceMessagesForMessageSendSafe:messageSend error:&deviceMessagesError];
if (deviceMessagesError || !deviceMessages) {
OWSAssertDebug(deviceMessagesError);
return failureHandler(deviceMessagesError);
}
if (messageSend.isLocalNumber) {
OWSAssertDebug([message isKindOfClass:[OWSOutgoingSyncMessage class]]);
// Messages sent to the "local number" should be sync messages.
//
@ -878,9 +921,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
OWSAssertDebug([message isKindOfClass:[OWSOutgoingSyncMessage class]]);
dispatch_async([OWSDispatch sendingQueue], ^{
// This emulates the completion logic of an actual successful save (see below).
// This emulates the completion logic of an actual successful send (see below).
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[message updateWithSkippedRecipient:localNumber transaction:transaction];
[message updateWithSkippedRecipient:messageSend.localNumber transaction:transaction];
}];
successHandler();
});
@ -918,51 +961,55 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
TSRequest *request = [OWSRequestFactory submitMessageRequestWithRecipient:recipient.uniqueId
messages:deviceMessages
timeStamp:message.timestamp];
if (useWebsocketIfAvailable && TSSocketManager.canMakeRequests) {
if (messageSend.isUDSend) {
DDLogVerbose(@"UD send.");
[request useUDAuth:messageSend.udAccessKey];
}
// TODO: UD sends over websocket.
if (!messageSend.hasWebsocketSendFailed && TSSocketManager.canMakeRequests && !messageSend.isUDSend) {
[TSSocketManager.sharedManager makeRequest:request
success:^(id _Nullable responseObject) {
[self messageSendDidSucceed:message
recipient:recipient
isLocalNumber:isLocalNumber
deviceMessages:deviceMessages
success:successHandler];
[self messageSendDidSucceed:messageSend deviceMessages:deviceMessages success:successHandler];
}
failure:^(NSInteger statusCode, NSData *_Nullable responseData, NSError *error) {
dispatch_async([OWSDispatch sendingQueue], ^{
OWSLogDebug(@"falling back to REST since first attempt failed.");
OWSLogDebug(@"Web socket send failed; failing over to REST.");
// Websockets can fail in different ways, so we don't decrement remainingAttempts for websocket
// failure. Instead we fall back to REST, which will decrement retries. e.g. after linking a new
// device, sync messages will fail until the websocket re-opens.
[self sendMessageToService:message
recipient:recipient
thread:thread
attempts:remainingAttemptsParam
useWebsocketIfAvailable:NO
success:successHandler
failure:failureHandler];
messageSend.hasWebsocketSendFailed = YES;
[self sendMessageToRecipient:messageSend success:successHandler failure:failureHandler];
});
}];
} else {
[self.networkManager makeRequest:request
success:^(NSURLSessionDataTask *task, id responseObject) {
[self messageSendDidSucceed:message
recipient:recipient
isLocalNumber:isLocalNumber
deviceMessages:deviceMessages
success:successHandler];
[self messageSendDidSucceed:messageSend deviceMessages:deviceMessages success:successHandler];
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
NSInteger statusCode = response.statusCode;
NSData *_Nullable responseData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
[self messageSendDidFail:message
recipient:recipient
thread:thread
isLocalNumber:isLocalNumber
if (messageSend.isUDSend && (statusCode == 401 || statusCode == 403)) {
// If a UD send fails due to service response (as opposed to network
// failure), mark recipient as _not_ in UD mode, then retry.
//
// TODO: Do we want to discriminate based on exact error?
OWSLogDebug(@"UD send failed; failing over to non-UD send.");
[self.udManager setSupportsUnidentifiedDelivery:NO recipientId:recipient.uniqueId];
messageSend.hasUDAuthFailed = YES;
dispatch_async([OWSDispatch sendingQueue], ^{
[self sendMessageToRecipient:messageSend success:successHandler failure:failureHandler];
});
return;
}
[self messageSendDidFail:messageSend
deviceMessages:deviceMessages
remainingAttempts:remainingAttempts
statusCode:statusCode
error:error
responseData:responseData
@ -972,20 +1019,19 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}
}
- (void)messageSendDidSucceed:(TSOutgoingMessage *)message
recipient:(SignalRecipient *)recipient
isLocalNumber:(BOOL)isLocalNumber
- (void)messageSendDidSucceed:(OWSMessageSend *)messageSend
deviceMessages:(NSArray<NSDictionary *> *)deviceMessages
success:(void (^)(void))successHandler
{
OWSAssertDebug(message);
OWSAssertDebug(recipient);
OWSAssertDebug(messageSend);
OWSAssertDebug(deviceMessages);
OWSAssertDebug(successHandler);
SignalRecipient *recipient = messageSend.recipient;
OWSLogInfo(@"Message send succeeded.");
if (isLocalNumber && deviceMessages.count == 0) {
if (messageSend.isLocalNumber && deviceMessages.count == 0) {
OWSLogInfo(@"Sent a message with no device messages; clearing 'mayHaveLinkedDevices'.");
// In order to avoid skipping necessary sync messages, the default value
// for mayHaveLinkedDevices is YES. Once we've successfully sent a
@ -999,42 +1045,40 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
dispatch_async([OWSDispatch sendingQueue], ^{
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[message updateWithSentRecipient:recipient.uniqueId transaction:transaction];
[messageSend.message updateWithSentRecipient:messageSend.recipient.uniqueId transaction:transaction];
// If we've just delivered a message to a user, we know they
// have a valid Signal account.
[SignalRecipient markRecipientAsRegisteredAndGet:recipient.recipientId transaction:transaction];
}];
[self handleMessageSentLocally:message];
[self handleMessageSentLocally:messageSend.message senderCertificate:messageSend.senderCertificate];
successHandler();
});
}
- (void)messageSendDidFail:(TSOutgoingMessage *)message
recipient:(SignalRecipient *)recipient
thread:(nullable TSThread *)thread
isLocalNumber:(BOOL)isLocalNumber
- (void)messageSendDidFail:(OWSMessageSend *)messageSend
deviceMessages:(NSArray<NSDictionary *> *)deviceMessages
remainingAttempts:(int)remainingAttempts
statusCode:(NSInteger)statusCode
error:(NSError *)responseError
responseData:(nullable NSData *)responseData
success:(void (^)(void))successHandler
failure:(RetryableFailureHandler)failureHandler
{
OWSAssertDebug(message);
OWSAssertDebug(recipient);
OWSAssertDebug(thread || [message isKindOfClass:[OWSOutgoingSyncMessage class]]);
OWSAssertDebug(messageSend);
OWSAssertDebug(messageSend.thread || [messageSend.message isKindOfClass:[OWSOutgoingSyncMessage class]]);
OWSAssertDebug(deviceMessages);
OWSAssertDebug(responseError);
OWSAssertDebug(successHandler);
OWSAssertDebug(failureHandler);
TSOutgoingMessage *message = messageSend.message;
SignalRecipient *recipient = messageSend.recipient;
OWSLogInfo(@"sending to recipient: %@, failed with error.", recipient.uniqueId);
void (^retrySend)(void) = ^void() {
if (remainingAttempts <= 0) {
if (messageSend.remainingAttempts <= 0) {
// Since we've already repeatedly failed to send to the messaging API,
// it's unlikely that repeating the whole process will succeed.
[responseError setIsRetryable:NO];
@ -1043,19 +1087,15 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
dispatch_async([OWSDispatch sendingQueue], ^{
OWSLogDebug(@"Retrying: %@", message.debugDescription);
[self sendMessageToService:message
recipient:recipient
thread:thread
attempts:remainingAttempts
useWebsocketIfAvailable:NO
success:successHandler
failure:failureHandler];
// TODO: Should this use sendMessageToRecipient or sendMessageToService?
[self sendMessageToRecipient:messageSend success:successHandler failure:failureHandler];
});
};
void (^handle404)(void) = ^{
OWSLogWarn(@"Unregistered recipient: %@", recipient.uniqueId);
TSThread *_Nullable thread = messageSend.thread;
OWSAssertDebug(thread);
dispatch_async([OWSDispatch sendingQueue], ^{
@ -1185,6 +1225,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}
- (void)handleMessageSentLocally:(TSOutgoingMessage *)message
senderCertificate:(nullable SMKSenderCertificate *)senderCertificate
{
if (message.shouldSyncTranscript) {
// TODO: I suspect we shouldn't optimistically set hasSyncedTranscript.
@ -1192,7 +1233,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[message updateWithHasSyncedTranscript:YES transaction:transaction];
}];
[self sendSyncTranscriptForMessage:message];
[self sendSyncTranscriptForMessage:message senderCertificate:senderCertificate];
}
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
@ -1203,21 +1244,25 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}
- (void)sendSyncTranscriptForMessage:(TSOutgoingMessage *)message
senderCertificate:(nullable SMKSenderCertificate *)senderCertificate
{
OWSOutgoingSentMessageTranscript *sentMessageTranscript =
[[OWSOutgoingSentMessageTranscript alloc] initWithOutgoingMessage:message];
NSString *recipientId = [TSAccountManager localNumber];
NSString *recipientId = self.tsAccountManager.localNumber;
__block SignalRecipient *recipient;
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
recipient = [SignalRecipient markRecipientAsRegisteredAndGet:recipientId transaction:transaction];
}];
[self sendMessageToService:sentMessageTranscript
recipient:recipient
thread:message.thread
attempts:OWSMessageSenderRetryAttempts
useWebsocketIfAvailable:YES
OWSMessageSend *messageSend = [[OWSMessageSend alloc] initWithMessage:sentMessageTranscript
thread:message.thread
recipient:recipient
senderCertificate:senderCertificate
udManager:self.udManager
localNumber:self.tsAccountManager.localNumber];
[self sendMessageToRecipient:messageSend
success:^{
OWSLogInfo(@"Successfully sent sync transcript.");
}
@ -1231,32 +1276,34 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}];
}
- (NSArray<NSDictionary *> *)deviceMessages:(TSOutgoingMessage *)message recipient:(SignalRecipient *)recipient
// NOTE: This method uses exceptions for control flow.
- (NSArray<NSDictionary *> *)deviceMessagesForMessageSendUnsafe:(OWSMessageSend *)messageSend
{
OWSAssertDebug(message);
OWSAssertDebug(recipient);
OWSAssertDebug(messageSend.message);
OWSAssertDebug(messageSend.recipient);
SignalRecipient *recipient = messageSend.recipient;
NSMutableArray *messagesArray = [NSMutableArray arrayWithCapacity:recipient.devices.count];
NSData *_Nullable plainText = [message buildPlainTextData:recipient];
NSData *_Nullable plainText = [messageSend.message buildPlainTextData:messageSend.recipient];
if (!plainText) {
OWSRaiseException(InvalidMessageException, @"Failed to build message proto");
}
OWSLogDebug(@"built message: %@ plainTextData.length: %lu", [message class], (unsigned long)plainText.length);
OWSLogDebug(
@"built message: %@ plainTextData.length: %lu", [messageSend.message class], (unsigned long)plainText.length);
for (NSNumber *deviceNumber in recipient.devices) {
for (NSNumber *deviceNumber in messageSend.recipient.devices) {
@try {
__block NSDictionary *messageDict;
__block NSException *encryptionException;
[self.dbConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
@try {
messageDict = [self encryptedMessageWithPlaintext:plainText
recipient:recipient
deviceId:deviceNumber
keyingStorage:self.primaryStorage
isSilent:message.isSilent
transaction:transaction];
messageDict = [self encryptedMessageForMessageSend:messageSend
deviceId:deviceNumber
plainText:plainText
transaction:transaction];
} @catch (NSException *exception) {
encryptionException = exception;
}
@ -1286,23 +1333,21 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
return [messagesArray copy];
}
- (NSDictionary *)encryptedMessageWithPlaintext:(NSData *)plainText
recipient:(SignalRecipient *)recipient
deviceId:(NSNumber *)deviceNumber
keyingStorage:(OWSPrimaryStorage *)storage
isSilent:(BOOL)isSilent
// NOTE: This method uses exceptions for control flow.
- (void)ensureRecipientHasSessionForMessageSend:(OWSMessageSend *)messageSend
deviceId:(NSNumber *)deviceId
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssertDebug(plainText);
OWSAssertDebug(recipient);
OWSAssertDebug(deviceNumber);
OWSAssertDebug(storage);
OWSAssertDebug(messageSend);
OWSAssertDebug(deviceId);
OWSAssertDebug(transaction);
NSString *identifier = recipient.recipientId;
OWSAssertDebug(identifier.length > 0);
OWSPrimaryStorage *storage = self.primaryStorage;
SignalRecipient *recipient = messageSend.recipient;
NSString *recipientId = recipient.recipientId;
OWSAssertDebug(recipientId.length > 0);
if (![storage containsSession:identifier deviceId:[deviceNumber intValue] protocolContext:transaction]) {
if (![storage containsSession:recipientId deviceId:[deviceId intValue] protocolContext:transaction]) {
__block dispatch_semaphore_t sema = dispatch_semaphore_create(0);
__block PreKeyBundle *_Nullable bundle;
__block NSException *_Nullable exception;
@ -1311,11 +1356,17 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
// are called _off_ the main thread. Otherwise we'll deadlock if the main
// thread is blocked on opening a transaction.
TSRequest *request =
[OWSRequestFactory recipientPrekeyRequestWithRecipient:identifier deviceId:[deviceNumber stringValue]];
[OWSRequestFactory recipientPrekeyRequestWithRecipient:recipientId deviceId:[deviceId stringValue]];
if (messageSend.isUDSend) {
DDLogVerbose(@"UD prekey request.");
[request useUDAuth:messageSend.udAccessKey];
}
[self.networkManager makeRequest:request
completionQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
success:^(NSURLSessionDataTask *task, id responseObject) {
bundle = [PreKeyBundle preKeyBundleFromDictionary:responseObject forDeviceNumber:deviceNumber];
bundle = [PreKeyBundle preKeyBundleFromDictionary:responseObject forDeviceNumber:deviceId];
dispatch_semaphore_signal(sema);
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
@ -1351,40 +1402,86 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
SessionBuilder *builder = [[SessionBuilder alloc] initWithSessionStore:storage
preKeyStore:storage
signedPreKeyStore:storage
identityKeyStore:[OWSIdentityManager sharedManager]
recipientId:identifier
deviceId:[deviceNumber intValue]];
identityKeyStore:self.identityManager
recipientId:recipientId
deviceId:[deviceId intValue]];
@try {
[builder processPrekeyBundle:bundle protocolContext:transaction];
} @catch (NSException *exception) {
if ([exception.name isEqualToString:UntrustedIdentityKeyException]) {
OWSRaiseExceptionWithUserInfo(UntrustedIdentityKeyException,
(@{ TSInvalidPreKeyBundleKey : bundle, TSInvalidRecipientKey : identifier }),
(@{ TSInvalidPreKeyBundleKey : bundle, TSInvalidRecipientKey : recipientId }),
@"");
}
@throw exception;
}
}
}
}
// NOTE: This method uses exceptions for control flow.
- (NSDictionary *)encryptedMessageForMessageSend:(OWSMessageSend *)messageSend
deviceId:(NSNumber *)deviceId
plainText:(NSData *)plainText
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssertDebug(messageSend);
OWSAssertDebug(deviceId);
OWSAssertDebug(plainText);
OWSAssertDebug(transaction);
OWSPrimaryStorage *storage = self.primaryStorage;
TSOutgoingMessage *message = messageSend.message;
SignalRecipient *recipient = messageSend.recipient;
NSString *recipientId = recipient.recipientId;
OWSAssertDebug(recipientId.length > 0);
// This may throw an exception.
[self ensureRecipientHasSessionForMessageSend:messageSend
deviceId:deviceId
transaction:transaction];
SessionCipher *cipher = [[SessionCipher alloc] initWithSessionStore:storage
preKeyStore:storage
signedPreKeyStore:storage
identityKeyStore:[OWSIdentityManager sharedManager]
recipientId:identifier
deviceId:[deviceNumber intValue]];
id<CipherMessage> encryptedMessage =
[cipher encryptMessage:[plainText paddedMessageBody] protocolContext:transaction];
identityKeyStore:self.identityManager
recipientId:recipientId
deviceId:[deviceId intValue]];
NSData *_Nullable serializedMessage;
TSWhisperMessageType messageType;
if (messageSend.isUDSend) {
NSError *error;
SMKSecretSessionCipher *_Nullable secretCipher =
[[SMKSecretSessionCipher alloc] initWithSessionStore:self.primaryStorage
preKeyStore:self.primaryStorage
signedPreKeyStore:self.primaryStorage
identityStore:self.identityManager
error:&error];
if (error || !secretCipher) {
OWSRaiseException(@"SecretSessionCipherFailure", @"Can't create secret session cipher.");
}
NSData *serializedMessage = encryptedMessage.serialized;
TSWhisperMessageType messageType = [self messageTypeForCipherMessage:encryptedMessage];
serializedMessage = [secretCipher encryptMessageWithRecipientId:recipientId
deviceId:deviceId.intValue
paddedPlaintext:[plainText paddedMessageBody]
senderCertificate:messageSend.senderCertificate
protocolContext:transaction
error:&error];
messageType = TSUnidentifiedSenderMessageType;
} else {
// This may throw an exception.
id<CipherMessage> encryptedMessage =
[cipher encryptMessage:[plainText paddedMessageBody] protocolContext:transaction];
serializedMessage = encryptedMessage.serialized;
messageType = [self messageTypeForCipherMessage:encryptedMessage];
}
BOOL isSilent = message.isSilent;
OWSMessageServiceParams *messageParams =
[[OWSMessageServiceParams alloc] initWithType:messageType
recipientId:identifier
device:[deviceNumber intValue]
recipientId:recipientId
device:[deviceId intValue]
content:serializedMessage
isSilent:isSilent
registrationId:[cipher remoteRegistrationId:transaction]];
@ -1412,12 +1509,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}
}
- (void)saveGroupMessage:(TSOutgoingMessage *)message inThread:(TSThread *)thread
- (void)saveInfoMessageForGroupMessage:(TSOutgoingMessage *)message inThread:(TSThread *)thread
{
if (message.groupMetaMessage == TSGroupMetaMessageDeliver) {
// TODO: Why is this necessary?
[message save];
} else if (message.groupMetaMessage == TSGroupMetaMessageQuit) {
if (message.groupMetaMessage == TSGroupMetaMessageQuit) {
[[[TSInfoMessage alloc] initWithTimestamp:message.timestamp
inThread:thread
messageType:TSInfoMessageTypeGroupQuit

@ -16,28 +16,34 @@ public enum OWSUDError: Error {
@objc func setup()
@objc func trustRoot() -> ECPublicKey
// MARK: - Recipient state
@objc func isUDRecipientId(_ recipientId: String) -> Bool
@objc func supportsUnidentifiedDelivery(recipientId: String) -> Bool
// No-op if this recipient id is already marked as a "UD recipient".
@objc func addUDRecipientId(_ recipientId: String)
@objc func setSupportsUnidentifiedDelivery(_ value: Bool, recipientId: String)
// No-op if this recipient id is already marked as _NOT_ a "UD recipient".
@objc func removeUDRecipientId(_ recipientId: String)
// Returns the UD access key for a given recipient if they are
// a UD recipient and we have a valid profile key for them.
@objc func udAccessKeyForRecipient(_ recipientId: String) -> SMKUDAccessKey?
// MARK: - Sender Certificate
// We use completion handlers instead of a promise so that message sending
// logic can access the certificate data.
@objc func ensureSenderCertificateObjC(success:@escaping (Data) -> Void,
@objc func ensureSenderCertificateObjC(success:@escaping (SMKSenderCertificate) -> Void,
failure:@escaping (Error) -> Void)
// MARK: - Unrestricted Access
@objc func shouldAllowUnrestrictedAccess() -> Bool
@objc func shouldAllowUnrestrictedAccessLocal() -> Bool
@objc func setShouldAllowUnrestrictedAccessLocal(_ value: Bool)
@objc func shouldAllowUnrestrictedAccess(recipientId: String) -> Bool
@objc func setShouldAllowUnrestrictedAccess(_ value: Bool)
@objc func setShouldAllowUnrestrictedAccess(recipientId: String, shouldAllowUnrestrictedAccess: Bool)
}
// MARK: -
@ -47,10 +53,11 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
private let dbConnection: YapDatabaseConnection
private let kUDRecipientModeCollection = "kUDRecipientModeCollection"
private let kUDCollection = "kUDCollection"
private let kUDCurrentSenderCertificateKey = "kUDCurrentSenderCertificateKey"
private let kUDUnrestrictedAccessKey = "kUDUnrestrictedAccessKey"
private let kUDRecipientModeCollection = "kUDRecipientModeCollection"
private let kUDUnrestrictedAccessCollection = "kUDUnrestrictedAccessCollection"
@objc
public required init(primaryStorage: OWSPrimaryStorage) {
@ -81,21 +88,46 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
ensureSenderCertificate().retainUntilComplete()
}
// MARK: - Dependencies
private var profileManager: ProfileManagerProtocol {
return SSKEnvironment.shared.profileManager
}
// MARK: - Recipient state
@objc
public func isUDRecipientId(_ recipientId: String) -> Bool {
public func supportsUnidentifiedDelivery(recipientId: String) -> Bool {
return dbConnection.bool(forKey: recipientId, inCollection: kUDRecipientModeCollection, defaultValue: false)
}
@objc
public func addUDRecipientId(_ recipientId: String) {
dbConnection.setBool(true, forKey: recipientId, inCollection: kUDRecipientModeCollection)
public func setSupportsUnidentifiedDelivery(_ value: Bool, recipientId: String) {
if value {
dbConnection.setBool(true, forKey: recipientId, inCollection: kUDRecipientModeCollection)
} else {
dbConnection.removeObject(forKey: recipientId, inCollection: kUDRecipientModeCollection)
}
}
// Returns the UD access key for a given recipient if they are
// a UD recipient and we have a valid profile key for them.
@objc
public func removeUDRecipientId(_ recipientId: String) {
dbConnection.removeObject(forKey: recipientId, inCollection: kUDRecipientModeCollection)
public func udAccessKeyForRecipient(_ recipientId: String) -> SMKUDAccessKey? {
guard supportsUnidentifiedDelivery(recipientId: recipientId) else {
return nil
}
guard let profileKey = profileManager.profileKeyData(forRecipientId: recipientId) else {
// Mark as "not a UD recipient".
return nil
}
do {
let udAccessKey = try SMKUDAccessKey(profileKey: profileKey)
return udAccessKey
} catch {
Logger.error("Could not determine udAccessKey: \(error)")
return nil
}
}
// MARK: - Sender Certificate
@ -107,17 +139,24 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
}
#endif
private func senderCertificate() -> Data? {
private func senderCertificate() -> SMKSenderCertificate? {
guard let certificateData = dbConnection.object(forKey: kUDCurrentSenderCertificateKey, inCollection: kUDCollection) as? Data else {
return nil
}
guard isValidCertificate(certificateData: certificateData) else {
Logger.warn("Current sender certificate is not valid.")
do {
let certificate = try SMKSenderCertificate.parse(data: certificateData)
guard isValidCertificate(certificate) else {
Logger.warn("Current sender certificate is not valid.")
return nil
}
return certificate
} catch {
owsFailDebug("Certificate could not be parsed: \(error)")
return nil
}
return certificateData
}
private func setSenderCertificate(_ certificateData: Data) {
@ -125,66 +164,97 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
}
@objc
public func ensureSenderCertificateObjC(success:@escaping (Data) -> Void,
failure:@escaping (Error) -> Void) {
public func ensureSenderCertificateObjC(success:@escaping (SMKSenderCertificate) -> Void,
failure:@escaping (Error) -> Void) {
ensureSenderCertificate()
.then(execute: { certificateData in
success(certificateData)
.then(execute: { certificate in
success(certificate)
})
.catch(execute: { (error) in
failure(error)
}).retainUntilComplete()
}
public func ensureSenderCertificate() -> Promise<Data> {
public func ensureSenderCertificate() -> Promise<SMKSenderCertificate> {
// If there is a valid cached sender certificate, use that.
if let certificateData = senderCertificate() {
return Promise(value: certificateData)
if let certificate = senderCertificate() {
return Promise(value: certificate)
}
// Try to obtain a new sender certificate.
return requestSenderCertificate().then { (certificateData) in
return requestSenderCertificate().then { (certificateData, certificate) in
// Cache the current sender certificate.
self.setSenderCertificate(certificateData)
return Promise(value: certificateData)
return Promise(value: certificate)
}
}
private func requestSenderCertificate() -> Promise<Data> {
return SignalServiceRestClient().requestUDSenderCertificate().then { (certificateData) in
guard self.isValidCertificate(certificateData: certificateData) else {
private func requestSenderCertificate() -> Promise<(Data, SMKSenderCertificate)> {
return SignalServiceRestClient().requestUDSenderCertificate().then { (certificateData) -> Promise<(Data, SMKSenderCertificate)> in
let certificate = try SMKSenderCertificate.parse(data: certificateData)
guard self.isValidCertificate(certificate) else {
throw OWSUDError.invalidData(description: "Invalid sender certificate returned by server")
}
return Promise(value: certificateData)
return Promise(value: (certificateData, certificate) )
}
}
private func isValidCertificate(certificateData: Data) -> Bool {
private func isValidCertificate(_ certificate: SMKSenderCertificate) -> Bool {
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: trustRoot())
// Ensure that the certificate will not expire in the next hour.
// We want a threshold long enough to ensure that any outgoing message
// sends will complete before the expiration.
let nowMs = NSDate.ows_millisecondTimeStamp()
let anHourFromNowMs = nowMs + kHourInMs
do {
let certificate = try SMKSenderCertificate.parse(data: certificateData)
let expirationMs = certificate.expirationTimestamp
let nowMs = NSDate.ows_millisecondTimeStamp()
// Ensure that the certificate will not expire in the next hour.
// We want a threshold long enough to ensure that any outgoing message
// sends will complete before the expiration.
let isValid = nowMs + kHourInMs < expirationMs
return isValid
try certificateValidator.validate(senderCertificate: certificate, validationTime: anHourFromNowMs)
return true
} catch {
OWSLogger.error("Certificate could not be parsed: \(error)")
OWSLogger.error("Invalid certificate")
return false
}
}
@objc
public func trustRoot() -> ECPublicKey {
guard let trustRootData = NSData(fromBase64String: kUDTrustRoot) else {
// This exits.
owsFail("Invalid trust root data.")
}
do {
return try ECPublicKey(serializedKeyData: trustRootData as Data)
} catch {
// This exits.
owsFail("Invalid trust root.")
}
}
// MARK: - Unrestricted Access
@objc
public func shouldAllowUnrestrictedAccess() -> Bool {
return dbConnection.bool(forKey: kUDUnrestrictedAccessKey, inCollection: kUDRecipientModeCollection, defaultValue: false)
public func shouldAllowUnrestrictedAccessLocal() -> Bool {
return dbConnection.bool(forKey: kUDUnrestrictedAccessKey, inCollection: kUDCollection, defaultValue: false)
}
@objc
public func setShouldAllowUnrestrictedAccessLocal(_ value: Bool) {
dbConnection.setBool(value, forKey: kUDUnrestrictedAccessKey, inCollection: kUDCollection)
}
@objc
public func shouldAllowUnrestrictedAccess(recipientId: String) -> Bool {
return dbConnection.bool(forKey: recipientId, inCollection: kUDUnrestrictedAccessCollection, defaultValue: false)
}
@objc
public func setShouldAllowUnrestrictedAccess(_ value: Bool) {
dbConnection.setBool(value, forKey: kUDUnrestrictedAccessKey, inCollection: kUDRecipientModeCollection)
public func setShouldAllowUnrestrictedAccess(recipientId: String, shouldAllowUnrestrictedAccess: Bool) {
dbConnection.setBool(shouldAllowUnrestrictedAccess, forKey: recipientId, inCollection: kUDUnrestrictedAccessCollection)
}
}

@ -22,6 +22,9 @@ typedef NS_ENUM(NSUInteger, TSVerificationTransport) { TSVerificationTransportVo
+ (TSRequest *)acknowledgeMessageDeliveryRequestWithSource:(NSString *)source timestamp:(UInt64)timestamp;
+ (TSRequest *)acknowledgeMessageDeliveryRequestWithServerGuid:(NSString *)serverGuid
serverTimestamp:(UInt64)serverTimestamp;
+ (TSRequest *)deleteDeviceRequestWithDevice:(OWSDevice *)device;
+ (TSRequest *)deviceProvisioningCodeRequest;

@ -72,6 +72,17 @@ NS_ASSUME_NONNULL_BEGIN
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"DELETE" parameters:@{}];
}
+ (TSRequest *)acknowledgeMessageDeliveryRequestWithServerGuid:(NSString *)serverGuid
serverTimestamp:(UInt64)serverTimestamp
{
OWSAssertDebug(serverGuid.length > 0);
OWSAssertDebug(serverTimestamp > 0);
NSString *path = [NSString stringWithFormat:@"v1/messages/%@/%llu", serverGuid, serverTimestamp];
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"DELETE" parameters:@{}];
}
+ (TSRequest *)deleteDeviceRequestWithDevice:(OWSDevice *)device
{
OWSAssertDebug(device);
@ -281,7 +292,7 @@ NS_ASSUME_NONNULL_BEGIN
// Crash app if UD cannot be enabled.
OWSFail(@"Could not determine UD access key: %@.", error);
}
BOOL allowUnrestrictedUD = [self.udManager shouldAllowUnrestrictedAccess] && udAccessKey != nil;
BOOL allowUnrestrictedUD = [self.udManager shouldAllowUnrestrictedAccessLocal] && udAccessKey != nil;
NSMutableDictionary *accountAttributes = [@{
@"signalingKey" : signalingKey,

@ -2,13 +2,15 @@
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
@class SMKUDAccessKey;
@interface TSRequest : NSMutableURLRequest
@property (nonatomic) BOOL shouldHaveAuthorizationHeaders;
@property (atomic, nullable) NSString *authUsername;
@property (atomic, nullable) NSString *authPassword;
@property (nonatomic, readonly) NSDictionary *parameters;
@property (nonatomic, readonly) NSDictionary<NSString *, id> *parameters;
- (instancetype)init NS_UNAVAILABLE;
@ -26,4 +28,8 @@
method:(NSString *)method
parameters:(nullable NSDictionary<NSString *, id> *)parameters;
#pragma mark - UD
- (void)useUDAuth:(SMKUDAccessKey *)udAccessKey;
@end

@ -5,6 +5,8 @@
#import "TSRequest.h"
#import "TSAccountManager.h"
#import "TSConstants.h"
#import <SignalCoreKit/NSData+OWS.h>
#import <SignalMetadataKit/SignalMetadataKit-Swift.h>
@implementation TSRequest
@ -110,4 +112,16 @@
}
}
#pragma mark - UD
- (void)useUDAuth:(SMKUDAccessKey *)udAccessKey
{
OWSAssertDebug(udAccessKey);
// Suppress normal auth headers.
self.shouldHaveAuthorizationHeaders = NO;
// Add UD auth header.
[self setValue:[udAccessKey.keyData base64EncodedString] forHTTPHeaderField:@"Unidentified-Access-Key"];
}
@end

@ -49,13 +49,19 @@ public enum SSKProtoError: Error {
// MARK: - SSKProtoEnvelopeBuilder
@objc public class func builder(type: SSKProtoEnvelopeType, source: String, sourceDevice: UInt32, timestamp: UInt64) -> SSKProtoEnvelopeBuilder {
return SSKProtoEnvelopeBuilder(type: type, source: source, sourceDevice: sourceDevice, timestamp: timestamp)
@objc public class func builder(type: SSKProtoEnvelopeType, timestamp: UInt64) -> SSKProtoEnvelopeBuilder {
return SSKProtoEnvelopeBuilder(type: type, timestamp: timestamp)
}
// asBuilder() constructs a builder that reflects the proto's contents.
@objc public func asBuilder() -> SSKProtoEnvelopeBuilder {
let builder = SSKProtoEnvelopeBuilder(type: type, source: source, sourceDevice: sourceDevice, timestamp: timestamp)
let builder = SSKProtoEnvelopeBuilder(type: type, timestamp: timestamp)
if let _value = source {
builder.setSource(_value)
}
if hasSourceDevice {
builder.setSourceDevice(sourceDevice)
}
if let _value = relay {
builder.setRelay(_value)
}
@ -80,12 +86,10 @@ public enum SSKProtoError: Error {
@objc fileprivate override init() {}
@objc fileprivate init(type: SSKProtoEnvelopeType, source: String, sourceDevice: UInt32, timestamp: UInt64) {
@objc fileprivate init(type: SSKProtoEnvelopeType, timestamp: UInt64) {
super.init()
setType(type)
setSource(source)
setSourceDevice(sourceDevice)
setTimestamp(timestamp)
}
@ -138,11 +142,24 @@ public enum SSKProtoError: Error {
@objc public let type: SSKProtoEnvelopeType
@objc public let source: String
@objc public let timestamp: UInt64
@objc public let sourceDevice: UInt32
@objc public var source: String? {
guard proto.hasSource else {
return nil
}
return proto.source
}
@objc public var hasSource: Bool {
return proto.hasSource
}
@objc public let timestamp: UInt64
@objc public var sourceDevice: UInt32 {
return proto.sourceDevice
}
@objc public var hasSourceDevice: Bool {
return proto.hasSourceDevice
}
@objc public var relay: String? {
guard proto.hasRelay else {
@ -193,13 +210,9 @@ public enum SSKProtoError: Error {
private init(proto: SignalServiceProtos_Envelope,
type: SSKProtoEnvelopeType,
source: String,
sourceDevice: UInt32,
timestamp: UInt64) {
self.proto = proto
self.type = type
self.source = source
self.sourceDevice = sourceDevice
self.timestamp = timestamp
}
@ -219,16 +232,6 @@ public enum SSKProtoError: Error {
}
let type = SSKProtoEnvelopeTypeWrap(proto.type)
guard proto.hasSource else {
throw SSKProtoError.invalidProtobuf(description: "\(logTag) missing required field: source")
}
let source = proto.source
guard proto.hasSourceDevice else {
throw SSKProtoError.invalidProtobuf(description: "\(logTag) missing required field: sourceDevice")
}
let sourceDevice = proto.sourceDevice
guard proto.hasTimestamp else {
throw SSKProtoError.invalidProtobuf(description: "\(logTag) missing required field: timestamp")
}
@ -240,8 +243,6 @@ public enum SSKProtoError: Error {
let result = SSKProtoEnvelope(proto: proto,
type: type,
source: source,
sourceDevice: sourceDevice,
timestamp: timestamp)
return result
}
@ -366,27 +367,27 @@ extension SSKProtoEnvelope.SSKProtoEnvelopeBuilder {
}
fileprivate class func parseProto(_ proto: SignalServiceProtos_Content) throws -> SSKProtoContent {
var dataMessage: SSKProtoDataMessage? = nil
var dataMessage: SSKProtoDataMessage?
if proto.hasDataMessage {
dataMessage = try SSKProtoDataMessage.parseProto(proto.dataMessage)
}
var syncMessage: SSKProtoSyncMessage? = nil
var syncMessage: SSKProtoSyncMessage?
if proto.hasSyncMessage {
syncMessage = try SSKProtoSyncMessage.parseProto(proto.syncMessage)
}
var callMessage: SSKProtoCallMessage? = nil
var callMessage: SSKProtoCallMessage?
if proto.hasCallMessage {
callMessage = try SSKProtoCallMessage.parseProto(proto.callMessage)
}
var nullMessage: SSKProtoNullMessage? = nil
var nullMessage: SSKProtoNullMessage?
if proto.hasNullMessage {
nullMessage = try SSKProtoNullMessage.parseProto(proto.nullMessage)
}
var receiptMessage: SSKProtoReceiptMessage? = nil
var receiptMessage: SSKProtoReceiptMessage?
if proto.hasReceiptMessage {
receiptMessage = try SSKProtoReceiptMessage.parseProto(proto.receiptMessage)
}
@ -1085,12 +1086,12 @@ extension SSKProtoCallMessageHangup.SSKProtoCallMessageHangupBuilder {
}
fileprivate class func parseProto(_ proto: SignalServiceProtos_CallMessage) throws -> SSKProtoCallMessage {
var offer: SSKProtoCallMessageOffer? = nil
var offer: SSKProtoCallMessageOffer?
if proto.hasOffer {
offer = try SSKProtoCallMessageOffer.parseProto(proto.offer)
}
var answer: SSKProtoCallMessageAnswer? = nil
var answer: SSKProtoCallMessageAnswer?
if proto.hasAnswer {
answer = try SSKProtoCallMessageAnswer.parseProto(proto.answer)
}
@ -1098,12 +1099,12 @@ extension SSKProtoCallMessageHangup.SSKProtoCallMessageHangupBuilder {
var iceUpdate: [SSKProtoCallMessageIceUpdate] = []
iceUpdate = try proto.iceUpdate.map { try SSKProtoCallMessageIceUpdate.parseProto($0) }
var hangup: SSKProtoCallMessageHangup? = nil
var hangup: SSKProtoCallMessageHangup?
if proto.hasHangup {
hangup = try SSKProtoCallMessageHangup.parseProto(proto.hangup)
}
var busy: SSKProtoCallMessageBusy? = nil
var busy: SSKProtoCallMessageBusy?
if proto.hasBusy {
busy = try SSKProtoCallMessageBusy.parseProto(proto.busy)
}
@ -1263,7 +1264,7 @@ extension SSKProtoCallMessage.SSKProtoCallMessageBuilder {
}
fileprivate class func parseProto(_ proto: SignalServiceProtos_DataMessage.Quote.QuotedAttachment) throws -> SSKProtoDataMessageQuoteQuotedAttachment {
var thumbnail: SSKProtoAttachmentPointer? = nil
var thumbnail: SSKProtoAttachmentPointer?
if proto.hasThumbnail {
thumbnail = try SSKProtoAttachmentPointer.parseProto(proto.thumbnail)
}
@ -2229,7 +2230,7 @@ extension SSKProtoDataMessageContactPostalAddress.SSKProtoDataMessageContactPost
}
fileprivate class func parseProto(_ proto: SignalServiceProtos_DataMessage.Contact.Avatar) throws -> SSKProtoDataMessageContactAvatar {
var avatar: SSKProtoAttachmentPointer? = nil
var avatar: SSKProtoAttachmentPointer?
if proto.hasAvatar {
avatar = try SSKProtoAttachmentPointer.parseProto(proto.avatar)
}
@ -2392,7 +2393,7 @@ extension SSKProtoDataMessageContactAvatar.SSKProtoDataMessageContactAvatarBuild
}
fileprivate class func parseProto(_ proto: SignalServiceProtos_DataMessage.Contact) throws -> SSKProtoDataMessageContact {
var name: SSKProtoDataMessageContactName? = nil
var name: SSKProtoDataMessageContactName?
if proto.hasName {
name = try SSKProtoDataMessageContactName.parseProto(proto.name)
}
@ -2406,7 +2407,7 @@ extension SSKProtoDataMessageContactAvatar.SSKProtoDataMessageContactAvatarBuild
var address: [SSKProtoDataMessageContactPostalAddress] = []
address = try proto.address.map { try SSKProtoDataMessageContactPostalAddress.parseProto($0) }
var avatar: SSKProtoDataMessageContactAvatar? = nil
var avatar: SSKProtoDataMessageContactAvatar?
if proto.hasAvatar {
avatar = try SSKProtoDataMessageContactAvatar.parseProto(proto.avatar)
}
@ -2644,12 +2645,12 @@ extension SSKProtoDataMessageContact.SSKProtoDataMessageContactBuilder {
var attachments: [SSKProtoAttachmentPointer] = []
attachments = try proto.attachments.map { try SSKProtoAttachmentPointer.parseProto($0) }
var group: SSKProtoGroupContext? = nil
var group: SSKProtoGroupContext?
if proto.hasGroup {
group = try SSKProtoGroupContext.parseProto(proto.group)
}
var quote: SSKProtoDataMessageQuote? = nil
var quote: SSKProtoDataMessageQuote?
if proto.hasQuote {
quote = try SSKProtoDataMessageQuote.parseProto(proto.quote)
}
@ -3174,7 +3175,7 @@ extension SSKProtoVerified.SSKProtoVerifiedBuilder {
}
fileprivate class func parseProto(_ proto: SignalServiceProtos_SyncMessage.Sent) throws -> SSKProtoSyncMessageSent {
var message: SSKProtoDataMessage? = nil
var message: SSKProtoDataMessage?
if proto.hasMessage {
message = try SSKProtoDataMessage.parseProto(proto.message)
}
@ -3371,7 +3372,7 @@ extension SSKProtoSyncMessageContacts.SSKProtoSyncMessageContactsBuilder {
}
fileprivate class func parseProto(_ proto: SignalServiceProtos_SyncMessage.Groups) throws -> SSKProtoSyncMessageGroups {
var blob: SSKProtoAttachmentPointer? = nil
var blob: SSKProtoAttachmentPointer?
if proto.hasBlob {
blob = try SSKProtoAttachmentPointer.parseProto(proto.blob)
}
@ -3980,22 +3981,22 @@ extension SSKProtoSyncMessageConfiguration.SSKProtoSyncMessageConfigurationBuild
}
fileprivate class func parseProto(_ proto: SignalServiceProtos_SyncMessage) throws -> SSKProtoSyncMessage {
var sent: SSKProtoSyncMessageSent? = nil
var sent: SSKProtoSyncMessageSent?
if proto.hasSent {
sent = try SSKProtoSyncMessageSent.parseProto(proto.sent)
}
var contacts: SSKProtoSyncMessageContacts? = nil
var contacts: SSKProtoSyncMessageContacts?
if proto.hasContacts {
contacts = try SSKProtoSyncMessageContacts.parseProto(proto.contacts)
}
var groups: SSKProtoSyncMessageGroups? = nil
var groups: SSKProtoSyncMessageGroups?
if proto.hasGroups {
groups = try SSKProtoSyncMessageGroups.parseProto(proto.groups)
}
var request: SSKProtoSyncMessageRequest? = nil
var request: SSKProtoSyncMessageRequest?
if proto.hasRequest {
request = try SSKProtoSyncMessageRequest.parseProto(proto.request)
}
@ -4003,17 +4004,17 @@ extension SSKProtoSyncMessageConfiguration.SSKProtoSyncMessageConfigurationBuild
var read: [SSKProtoSyncMessageRead] = []
read = try proto.read.map { try SSKProtoSyncMessageRead.parseProto($0) }
var blocked: SSKProtoSyncMessageBlocked? = nil
var blocked: SSKProtoSyncMessageBlocked?
if proto.hasBlocked {
blocked = try SSKProtoSyncMessageBlocked.parseProto(proto.blocked)
}
var verified: SSKProtoVerified? = nil
var verified: SSKProtoVerified?
if proto.hasVerified {
verified = try SSKProtoVerified.parseProto(proto.verified)
}
var configuration: SSKProtoSyncMessageConfiguration? = nil
var configuration: SSKProtoSyncMessageConfiguration?
if proto.hasConfiguration {
configuration = try SSKProtoSyncMessageConfiguration.parseProto(proto.configuration)
}
@ -4457,7 +4458,7 @@ extension SSKProtoAttachmentPointer.SSKProtoAttachmentPointerBuilder {
}
let type = SSKProtoGroupContextTypeWrap(proto.type)
var avatar: SSKProtoAttachmentPointer? = nil
var avatar: SSKProtoAttachmentPointer?
if proto.hasAvatar {
avatar = try SSKProtoAttachmentPointer.parseProto(proto.avatar)
}
@ -4762,12 +4763,12 @@ extension SSKProtoContactDetailsAvatar.SSKProtoContactDetailsAvatarBuilder {
}
let number = proto.number
var avatar: SSKProtoContactDetailsAvatar? = nil
var avatar: SSKProtoContactDetailsAvatar?
if proto.hasAvatar {
avatar = try SSKProtoContactDetailsAvatar.parseProto(proto.avatar)
}
var verified: SSKProtoVerified? = nil
var verified: SSKProtoVerified?
if proto.hasVerified {
verified = try SSKProtoVerified.parseProto(proto.verified)
}
@ -5073,7 +5074,7 @@ extension SSKProtoGroupDetailsAvatar.SSKProtoGroupDetailsAvatarBuilder {
}
let id = proto.id
var avatar: SSKProtoGroupDetailsAvatar? = nil
var avatar: SSKProtoGroupDetailsAvatar?
if proto.hasAvatar {
avatar = try SSKProtoGroupDetailsAvatar.parseProto(proto.avatar)
}

@ -1,10 +1,6 @@
// DO NOT EDIT.
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: SignalService.proto
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
// For information on using the generated types, please see the documenation:
// https://github.com/apple/swift-protobuf/
//*
// Copyright (C) 2014-2016 Open Whisper Systems
@ -22,7 +18,7 @@ import SwiftProtobuf
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that your are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
@ -42,7 +38,6 @@ struct SignalServiceProtos_Envelope {
/// Clears the value of `type`. Subsequent reads from it will return its default value.
mutating func clearType() {self._type = nil}
/// @required
var source: String {
get {return _source ?? String()}
set {_source = newValue}
@ -52,7 +47,6 @@ struct SignalServiceProtos_Envelope {
/// Clears the value of `source`. Subsequent reads from it will return its default value.
mutating func clearSource() {self._source = nil}
/// @required
var sourceDevice: UInt32 {
get {return _sourceDevice ?? 0}
set {_sourceDevice = newValue}
@ -163,15 +157,15 @@ struct SignalServiceProtos_Envelope {
init() {}
fileprivate var _type: SignalServiceProtos_Envelope.TypeEnum? = nil
fileprivate var _source: String? = nil
fileprivate var _sourceDevice: UInt32? = nil
fileprivate var _relay: String? = nil
fileprivate var _timestamp: UInt64? = nil
fileprivate var _legacyMessage: Data? = nil
fileprivate var _content: Data? = nil
fileprivate var _serverGuid: String? = nil
fileprivate var _serverTimestamp: UInt64? = nil
fileprivate var _type: SignalServiceProtos_Envelope.TypeEnum?
fileprivate var _source: String?
fileprivate var _sourceDevice: UInt32?
fileprivate var _relay: String?
fileprivate var _timestamp: UInt64?
fileprivate var _legacyMessage: Data?
fileprivate var _content: Data?
fileprivate var _serverGuid: String?
fileprivate var _serverTimestamp: UInt64?
}
#if swift(>=4.2)
@ -329,8 +323,8 @@ struct SignalServiceProtos_CallMessage {
init() {}
fileprivate var _id: UInt64? = nil
fileprivate var _sessionDescription: String? = nil
fileprivate var _id: UInt64?
fileprivate var _sessionDescription: String?
}
struct Answer {
@ -364,8 +358,8 @@ struct SignalServiceProtos_CallMessage {
init() {}
fileprivate var _id: UInt64? = nil
fileprivate var _sessionDescription: String? = nil
fileprivate var _id: UInt64?
fileprivate var _sessionDescription: String?
}
struct IceUpdate {
@ -417,10 +411,10 @@ struct SignalServiceProtos_CallMessage {
init() {}
fileprivate var _id: UInt64? = nil
fileprivate var _sdpMid: String? = nil
fileprivate var _sdpMlineIndex: UInt32? = nil
fileprivate var _sdp: String? = nil
fileprivate var _id: UInt64?
fileprivate var _sdpMid: String?
fileprivate var _sdpMlineIndex: UInt32?
fileprivate var _sdp: String?
}
struct Busy {
@ -442,7 +436,7 @@ struct SignalServiceProtos_CallMessage {
init() {}
fileprivate var _id: UInt64? = nil
fileprivate var _id: UInt64?
}
struct Hangup {
@ -464,7 +458,7 @@ struct SignalServiceProtos_CallMessage {
init() {}
fileprivate var _id: UInt64? = nil
fileprivate var _id: UInt64?
}
init() {}
@ -692,9 +686,9 @@ struct SignalServiceProtos_DataMessage {
init() {}
fileprivate var _id: UInt64? = nil
fileprivate var _author: String? = nil
fileprivate var _text: String? = nil
fileprivate var _id: UInt64?
fileprivate var _author: String?
fileprivate var _text: String?
}
struct Contact {
@ -809,12 +803,12 @@ struct SignalServiceProtos_DataMessage {
init() {}
fileprivate var _givenName: String? = nil
fileprivate var _familyName: String? = nil
fileprivate var _prefix: String? = nil
fileprivate var _suffix: String? = nil
fileprivate var _middleName: String? = nil
fileprivate var _displayName: String? = nil
fileprivate var _givenName: String?
fileprivate var _familyName: String?
fileprivate var _prefix: String?
fileprivate var _suffix: String?
fileprivate var _middleName: String?
fileprivate var _displayName: String?
}
struct Phone {
@ -885,9 +879,9 @@ struct SignalServiceProtos_DataMessage {
init() {}
fileprivate var _value: String? = nil
fileprivate var _type: SignalServiceProtos_DataMessage.Contact.Phone.TypeEnum? = nil
fileprivate var _label: String? = nil
fileprivate var _value: String?
fileprivate var _type: SignalServiceProtos_DataMessage.Contact.Phone.TypeEnum?
fileprivate var _label: String?
}
struct Email {
@ -958,9 +952,9 @@ struct SignalServiceProtos_DataMessage {
init() {}
fileprivate var _value: String? = nil
fileprivate var _type: SignalServiceProtos_DataMessage.Contact.Email.TypeEnum? = nil
fileprivate var _label: String? = nil
fileprivate var _value: String?
fileprivate var _type: SignalServiceProtos_DataMessage.Contact.Email.TypeEnum?
fileprivate var _label: String?
}
struct PostalAddress {
@ -1082,15 +1076,15 @@ struct SignalServiceProtos_DataMessage {
init() {}
fileprivate var _type: SignalServiceProtos_DataMessage.Contact.PostalAddress.TypeEnum? = nil
fileprivate var _label: String? = nil
fileprivate var _street: String? = nil
fileprivate var _pobox: String? = nil
fileprivate var _neighborhood: String? = nil
fileprivate var _city: String? = nil
fileprivate var _region: String? = nil
fileprivate var _postcode: String? = nil
fileprivate var _country: String? = nil
fileprivate var _type: SignalServiceProtos_DataMessage.Contact.PostalAddress.TypeEnum?
fileprivate var _label: String?
fileprivate var _street: String?
fileprivate var _pobox: String?
fileprivate var _neighborhood: String?
fileprivate var _city: String?
fileprivate var _region: String?
fileprivate var _postcode: String?
fileprivate var _country: String?
}
struct Avatar {
@ -1159,7 +1153,7 @@ struct SignalServiceProtos_NullMessage {
init() {}
fileprivate var _padding: Data? = nil
fileprivate var _padding: Data?
}
struct SignalServiceProtos_ReceiptMessage {
@ -1209,7 +1203,7 @@ struct SignalServiceProtos_ReceiptMessage {
init() {}
fileprivate var _type: SignalServiceProtos_ReceiptMessage.TypeEnum? = nil
fileprivate var _type: SignalServiceProtos_ReceiptMessage.TypeEnum?
}
#if swift(>=4.2)
@ -1295,10 +1289,10 @@ struct SignalServiceProtos_Verified {
init() {}
fileprivate var _destination: String? = nil
fileprivate var _identityKey: Data? = nil
fileprivate var _state: SignalServiceProtos_Verified.State? = nil
fileprivate var _nullMessage: Data? = nil
fileprivate var _destination: String?
fileprivate var _identityKey: Data?
fileprivate var _state: SignalServiceProtos_Verified.State?
fileprivate var _nullMessage: Data?
}
#if swift(>=4.2)
@ -1562,7 +1556,7 @@ struct SignalServiceProtos_SyncMessage {
init() {}
fileprivate var _type: SignalServiceProtos_SyncMessage.Request.TypeEnum? = nil
fileprivate var _type: SignalServiceProtos_SyncMessage.Request.TypeEnum?
}
struct Read {
@ -1594,8 +1588,8 @@ struct SignalServiceProtos_SyncMessage {
init() {}
fileprivate var _sender: String? = nil
fileprivate var _timestamp: UInt64? = nil
fileprivate var _sender: String?
fileprivate var _timestamp: UInt64?
}
struct Configuration {
@ -1616,7 +1610,7 @@ struct SignalServiceProtos_SyncMessage {
init() {}
fileprivate var _readReceipts: Bool? = nil
fileprivate var _readReceipts: Bool?
}
init() {}
@ -1747,16 +1741,16 @@ struct SignalServiceProtos_AttachmentPointer {
init() {}
fileprivate var _id: UInt64? = nil
fileprivate var _contentType: String? = nil
fileprivate var _key: Data? = nil
fileprivate var _size: UInt32? = nil
fileprivate var _thumbnail: Data? = nil
fileprivate var _digest: Data? = nil
fileprivate var _fileName: String? = nil
fileprivate var _flags: UInt32? = nil
fileprivate var _width: UInt32? = nil
fileprivate var _height: UInt32? = nil
fileprivate var _id: UInt64?
fileprivate var _contentType: String?
fileprivate var _key: Data?
fileprivate var _size: UInt32?
fileprivate var _thumbnail: Data?
fileprivate var _digest: Data?
fileprivate var _fileName: String?
fileprivate var _flags: UInt32?
fileprivate var _width: UInt32?
fileprivate var _height: UInt32?
}
#if swift(>=4.2)
@ -1972,8 +1966,8 @@ struct SignalServiceProtos_ContactDetails {
init() {}
fileprivate var _contentType: String? = nil
fileprivate var _length: UInt32? = nil
fileprivate var _contentType: String?
fileprivate var _length: UInt32?
}
init() {}
@ -2084,8 +2078,8 @@ struct SignalServiceProtos_GroupDetails {
init() {}
fileprivate var _contentType: String? = nil
fileprivate var _length: UInt32? = nil
fileprivate var _contentType: String?
fileprivate var _length: UInt32?
}
init() {}
@ -2095,7 +2089,7 @@ struct SignalServiceProtos_GroupDetails {
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "SignalServiceProtos"
private let _protobuf_package = "SignalServiceProtos"
extension SignalServiceProtos_Envelope: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".Envelope"
@ -2108,7 +2102,7 @@ extension SignalServiceProtos_Envelope: SwiftProtobuf.Message, SwiftProtobuf._Me
6: .same(proto: "legacyMessage"),
8: .same(proto: "content"),
9: .same(proto: "serverGuid"),
10: .same(proto: "serverTimestamp"),
10: .same(proto: "serverTimestamp")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -2181,7 +2175,7 @@ extension SignalServiceProtos_Envelope.TypeEnum: SwiftProtobuf._ProtoNameProvidi
2: .same(proto: "KEY_EXCHANGE"),
3: .same(proto: "PREKEY_BUNDLE"),
5: .same(proto: "RECEIPT"),
6: .same(proto: "UNIDENTIFIED_SENDER"),
6: .same(proto: "UNIDENTIFIED_SENDER")
]
}
@ -2192,15 +2186,15 @@ extension SignalServiceProtos_Content: SwiftProtobuf.Message, SwiftProtobuf._Mes
2: .same(proto: "syncMessage"),
3: .same(proto: "callMessage"),
4: .same(proto: "nullMessage"),
5: .same(proto: "receiptMessage"),
5: .same(proto: "receiptMessage")
]
fileprivate class _StorageClass {
var _dataMessage: SignalServiceProtos_DataMessage? = nil
var _syncMessage: SignalServiceProtos_SyncMessage? = nil
var _callMessage: SignalServiceProtos_CallMessage? = nil
var _nullMessage: SignalServiceProtos_NullMessage? = nil
var _receiptMessage: SignalServiceProtos_ReceiptMessage? = nil
var _dataMessage: SignalServiceProtos_DataMessage?
var _syncMessage: SignalServiceProtos_SyncMessage?
var _callMessage: SignalServiceProtos_CallMessage?
var _nullMessage: SignalServiceProtos_NullMessage?
var _receiptMessage: SignalServiceProtos_ReceiptMessage?
static let defaultInstance = _StorageClass()
@ -2286,16 +2280,16 @@ extension SignalServiceProtos_CallMessage: SwiftProtobuf.Message, SwiftProtobuf.
3: .same(proto: "iceUpdate"),
4: .same(proto: "hangup"),
5: .same(proto: "busy"),
6: .same(proto: "profileKey"),
6: .same(proto: "profileKey")
]
fileprivate class _StorageClass {
var _offer: SignalServiceProtos_CallMessage.Offer? = nil
var _answer: SignalServiceProtos_CallMessage.Answer? = nil
var _offer: SignalServiceProtos_CallMessage.Offer?
var _answer: SignalServiceProtos_CallMessage.Answer?
var _iceUpdate: [SignalServiceProtos_CallMessage.IceUpdate] = []
var _hangup: SignalServiceProtos_CallMessage.Hangup? = nil
var _busy: SignalServiceProtos_CallMessage.Busy? = nil
var _profileKey: Data? = nil
var _hangup: SignalServiceProtos_CallMessage.Hangup?
var _busy: SignalServiceProtos_CallMessage.Busy?
var _profileKey: Data?
static let defaultInstance = _StorageClass()
@ -2383,7 +2377,7 @@ extension SignalServiceProtos_CallMessage.Offer: SwiftProtobuf.Message, SwiftPro
static let protoMessageName: String = SignalServiceProtos_CallMessage.protoMessageName + ".Offer"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
2: .same(proto: "sessionDescription"),
2: .same(proto: "sessionDescription")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -2418,7 +2412,7 @@ extension SignalServiceProtos_CallMessage.Answer: SwiftProtobuf.Message, SwiftPr
static let protoMessageName: String = SignalServiceProtos_CallMessage.protoMessageName + ".Answer"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
2: .same(proto: "sessionDescription"),
2: .same(proto: "sessionDescription")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -2455,7 +2449,7 @@ extension SignalServiceProtos_CallMessage.IceUpdate: SwiftProtobuf.Message, Swif
1: .same(proto: "id"),
2: .same(proto: "sdpMid"),
3: .same(proto: "sdpMLineIndex"),
4: .same(proto: "sdp"),
4: .same(proto: "sdp")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -2499,7 +2493,7 @@ extension SignalServiceProtos_CallMessage.IceUpdate: SwiftProtobuf.Message, Swif
extension SignalServiceProtos_CallMessage.Busy: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = SignalServiceProtos_CallMessage.protoMessageName + ".Busy"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
1: .same(proto: "id")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -2528,7 +2522,7 @@ extension SignalServiceProtos_CallMessage.Busy: SwiftProtobuf.Message, SwiftProt
extension SignalServiceProtos_CallMessage.Hangup: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = SignalServiceProtos_CallMessage.protoMessageName + ".Hangup"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
1: .same(proto: "id")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -2565,18 +2559,18 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf.
6: .same(proto: "profileKey"),
7: .same(proto: "timestamp"),
8: .same(proto: "quote"),
9: .same(proto: "contact"),
9: .same(proto: "contact")
]
fileprivate class _StorageClass {
var _body: String? = nil
var _body: String?
var _attachments: [SignalServiceProtos_AttachmentPointer] = []
var _group: SignalServiceProtos_GroupContext? = nil
var _flags: UInt32? = nil
var _expireTimer: UInt32? = nil
var _profileKey: Data? = nil
var _timestamp: UInt64? = nil
var _quote: SignalServiceProtos_DataMessage.Quote? = nil
var _group: SignalServiceProtos_GroupContext?
var _flags: UInt32?
var _expireTimer: UInt32?
var _profileKey: Data?
var _timestamp: UInt64?
var _quote: SignalServiceProtos_DataMessage.Quote?
var _contact: [SignalServiceProtos_DataMessage.Contact] = []
static let defaultInstance = _StorageClass()
@ -2683,7 +2677,7 @@ extension SignalServiceProtos_DataMessage.Flags: SwiftProtobuf._ProtoNameProvidi
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "END_SESSION"),
2: .same(proto: "EXPIRATION_TIMER_UPDATE"),
4: .same(proto: "PROFILE_KEY_UPDATE"),
4: .same(proto: "PROFILE_KEY_UPDATE")
]
}
@ -2693,7 +2687,7 @@ extension SignalServiceProtos_DataMessage.Quote: SwiftProtobuf.Message, SwiftPro
1: .same(proto: "id"),
2: .same(proto: "author"),
3: .same(proto: "text"),
4: .same(proto: "attachments"),
4: .same(proto: "attachments")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -2740,14 +2734,14 @@ extension SignalServiceProtos_DataMessage.Quote.QuotedAttachment: SwiftProtobuf.
1: .same(proto: "contentType"),
2: .same(proto: "fileName"),
3: .same(proto: "thumbnail"),
4: .same(proto: "flags"),
4: .same(proto: "flags")
]
fileprivate class _StorageClass {
var _contentType: String? = nil
var _fileName: String? = nil
var _thumbnail: SignalServiceProtos_AttachmentPointer? = nil
var _flags: UInt32? = nil
var _contentType: String?
var _fileName: String?
var _thumbnail: SignalServiceProtos_AttachmentPointer?
var _flags: UInt32?
static let defaultInstance = _StorageClass()
@ -2821,7 +2815,7 @@ extension SignalServiceProtos_DataMessage.Quote.QuotedAttachment: SwiftProtobuf.
extension SignalServiceProtos_DataMessage.Quote.QuotedAttachment.Flags: SwiftProtobuf._ProtoNameProviding {
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "VOICE_MESSAGE"),
1: .same(proto: "VOICE_MESSAGE")
]
}
@ -2833,16 +2827,16 @@ extension SignalServiceProtos_DataMessage.Contact: SwiftProtobuf.Message, SwiftP
4: .same(proto: "email"),
5: .same(proto: "address"),
6: .same(proto: "avatar"),
7: .same(proto: "organization"),
7: .same(proto: "organization")
]
fileprivate class _StorageClass {
var _name: SignalServiceProtos_DataMessage.Contact.Name? = nil
var _name: SignalServiceProtos_DataMessage.Contact.Name?
var _number: [SignalServiceProtos_DataMessage.Contact.Phone] = []
var _email: [SignalServiceProtos_DataMessage.Contact.Email] = []
var _address: [SignalServiceProtos_DataMessage.Contact.PostalAddress] = []
var _avatar: SignalServiceProtos_DataMessage.Contact.Avatar? = nil
var _organization: String? = nil
var _avatar: SignalServiceProtos_DataMessage.Contact.Avatar?
var _organization: String?
static let defaultInstance = _StorageClass()
@ -2934,7 +2928,7 @@ extension SignalServiceProtos_DataMessage.Contact.Name: SwiftProtobuf.Message, S
3: .same(proto: "prefix"),
4: .same(proto: "suffix"),
5: .same(proto: "middleName"),
6: .same(proto: "displayName"),
6: .same(proto: "displayName")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -2990,7 +2984,7 @@ extension SignalServiceProtos_DataMessage.Contact.Phone: SwiftProtobuf.Message,
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "value"),
2: .same(proto: "type"),
3: .same(proto: "label"),
3: .same(proto: "label")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -3031,7 +3025,7 @@ extension SignalServiceProtos_DataMessage.Contact.Phone.TypeEnum: SwiftProtobuf.
1: .same(proto: "HOME"),
2: .same(proto: "MOBILE"),
3: .same(proto: "WORK"),
4: .same(proto: "CUSTOM"),
4: .same(proto: "CUSTOM")
]
}
@ -3040,7 +3034,7 @@ extension SignalServiceProtos_DataMessage.Contact.Email: SwiftProtobuf.Message,
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "value"),
2: .same(proto: "type"),
3: .same(proto: "label"),
3: .same(proto: "label")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -3081,7 +3075,7 @@ extension SignalServiceProtos_DataMessage.Contact.Email.TypeEnum: SwiftProtobuf.
1: .same(proto: "HOME"),
2: .same(proto: "MOBILE"),
3: .same(proto: "WORK"),
4: .same(proto: "CUSTOM"),
4: .same(proto: "CUSTOM")
]
}
@ -3096,7 +3090,7 @@ extension SignalServiceProtos_DataMessage.Contact.PostalAddress: SwiftProtobuf.M
6: .same(proto: "city"),
7: .same(proto: "region"),
8: .same(proto: "postcode"),
9: .same(proto: "country"),
9: .same(proto: "country")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -3166,7 +3160,7 @@ extension SignalServiceProtos_DataMessage.Contact.PostalAddress.TypeEnum: SwiftP
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "HOME"),
2: .same(proto: "WORK"),
3: .same(proto: "CUSTOM"),
3: .same(proto: "CUSTOM")
]
}
@ -3174,12 +3168,12 @@ extension SignalServiceProtos_DataMessage.Contact.Avatar: SwiftProtobuf.Message,
static let protoMessageName: String = SignalServiceProtos_DataMessage.Contact.protoMessageName + ".Avatar"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "avatar"),
2: .same(proto: "isProfile"),
2: .same(proto: "isProfile")
]
fileprivate class _StorageClass {
var _avatar: SignalServiceProtos_AttachmentPointer? = nil
var _isProfile: Bool? = nil
var _avatar: SignalServiceProtos_AttachmentPointer?
var _isProfile: Bool?
static let defaultInstance = _StorageClass()
@ -3242,7 +3236,7 @@ extension SignalServiceProtos_DataMessage.Contact.Avatar: SwiftProtobuf.Message,
extension SignalServiceProtos_NullMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".NullMessage"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "padding"),
1: .same(proto: "padding")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -3272,7 +3266,7 @@ extension SignalServiceProtos_ReceiptMessage: SwiftProtobuf.Message, SwiftProtob
static let protoMessageName: String = _protobuf_package + ".ReceiptMessage"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "type"),
2: .same(proto: "timestamp"),
2: .same(proto: "timestamp")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -3306,7 +3300,7 @@ extension SignalServiceProtos_ReceiptMessage: SwiftProtobuf.Message, SwiftProtob
extension SignalServiceProtos_ReceiptMessage.TypeEnum: SwiftProtobuf._ProtoNameProviding {
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "DELIVERY"),
1: .same(proto: "READ"),
1: .same(proto: "READ")
]
}
@ -3316,7 +3310,7 @@ extension SignalServiceProtos_Verified: SwiftProtobuf.Message, SwiftProtobuf._Me
1: .same(proto: "destination"),
2: .same(proto: "identityKey"),
3: .same(proto: "state"),
4: .same(proto: "nullMessage"),
4: .same(proto: "nullMessage")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -3361,7 +3355,7 @@ extension SignalServiceProtos_Verified.State: SwiftProtobuf._ProtoNameProviding
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "DEFAULT"),
1: .same(proto: "VERIFIED"),
2: .same(proto: "UNVERIFIED"),
2: .same(proto: "UNVERIFIED")
]
}
@ -3376,19 +3370,19 @@ extension SignalServiceProtos_SyncMessage: SwiftProtobuf.Message, SwiftProtobuf.
6: .same(proto: "blocked"),
7: .same(proto: "verified"),
9: .same(proto: "configuration"),
8: .same(proto: "padding"),
8: .same(proto: "padding")
]
fileprivate class _StorageClass {
var _sent: SignalServiceProtos_SyncMessage.Sent? = nil
var _contacts: SignalServiceProtos_SyncMessage.Contacts? = nil
var _groups: SignalServiceProtos_SyncMessage.Groups? = nil
var _request: SignalServiceProtos_SyncMessage.Request? = nil
var _sent: SignalServiceProtos_SyncMessage.Sent?
var _contacts: SignalServiceProtos_SyncMessage.Contacts?
var _groups: SignalServiceProtos_SyncMessage.Groups?
var _request: SignalServiceProtos_SyncMessage.Request?
var _read: [SignalServiceProtos_SyncMessage.Read] = []
var _blocked: SignalServiceProtos_SyncMessage.Blocked? = nil
var _verified: SignalServiceProtos_Verified? = nil
var _configuration: SignalServiceProtos_SyncMessage.Configuration? = nil
var _padding: Data? = nil
var _blocked: SignalServiceProtos_SyncMessage.Blocked?
var _verified: SignalServiceProtos_Verified?
var _configuration: SignalServiceProtos_SyncMessage.Configuration?
var _padding: Data?
static let defaultInstance = _StorageClass()
@ -3496,14 +3490,14 @@ extension SignalServiceProtos_SyncMessage.Sent: SwiftProtobuf.Message, SwiftProt
1: .same(proto: "destination"),
2: .same(proto: "timestamp"),
3: .same(proto: "message"),
4: .same(proto: "expirationStartTimestamp"),
4: .same(proto: "expirationStartTimestamp")
]
fileprivate class _StorageClass {
var _destination: String? = nil
var _timestamp: UInt64? = nil
var _message: SignalServiceProtos_DataMessage? = nil
var _expirationStartTimestamp: UInt64? = nil
var _destination: String?
var _timestamp: UInt64?
var _message: SignalServiceProtos_DataMessage?
var _expirationStartTimestamp: UInt64?
static let defaultInstance = _StorageClass()
@ -3579,12 +3573,12 @@ extension SignalServiceProtos_SyncMessage.Contacts: SwiftProtobuf.Message, Swift
static let protoMessageName: String = SignalServiceProtos_SyncMessage.protoMessageName + ".Contacts"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "blob"),
2: .same(proto: "isComplete"),
2: .same(proto: "isComplete")
]
fileprivate class _StorageClass {
var _blob: SignalServiceProtos_AttachmentPointer? = nil
var _isComplete: Bool? = nil
var _blob: SignalServiceProtos_AttachmentPointer?
var _isComplete: Bool?
static let defaultInstance = _StorageClass()
@ -3647,11 +3641,11 @@ extension SignalServiceProtos_SyncMessage.Contacts: SwiftProtobuf.Message, Swift
extension SignalServiceProtos_SyncMessage.Groups: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = SignalServiceProtos_SyncMessage.protoMessageName + ".Groups"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "blob"),
1: .same(proto: "blob")
]
fileprivate class _StorageClass {
var _blob: SignalServiceProtos_AttachmentPointer? = nil
var _blob: SignalServiceProtos_AttachmentPointer?
static let defaultInstance = _StorageClass()
@ -3709,7 +3703,7 @@ extension SignalServiceProtos_SyncMessage.Blocked: SwiftProtobuf.Message, SwiftP
static let protoMessageName: String = SignalServiceProtos_SyncMessage.protoMessageName + ".Blocked"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "numbers"),
2: .same(proto: "groupIds"),
2: .same(proto: "groupIds")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -3743,7 +3737,7 @@ extension SignalServiceProtos_SyncMessage.Blocked: SwiftProtobuf.Message, SwiftP
extension SignalServiceProtos_SyncMessage.Request: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = SignalServiceProtos_SyncMessage.protoMessageName + ".Request"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "type"),
1: .same(proto: "type")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -3775,7 +3769,7 @@ extension SignalServiceProtos_SyncMessage.Request.TypeEnum: SwiftProtobuf._Proto
1: .same(proto: "CONTACTS"),
2: .same(proto: "GROUPS"),
3: .same(proto: "BLOCKED"),
4: .same(proto: "CONFIGURATION"),
4: .same(proto: "CONFIGURATION")
]
}
@ -3783,7 +3777,7 @@ extension SignalServiceProtos_SyncMessage.Read: SwiftProtobuf.Message, SwiftProt
static let protoMessageName: String = SignalServiceProtos_SyncMessage.protoMessageName + ".Read"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "sender"),
2: .same(proto: "timestamp"),
2: .same(proto: "timestamp")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -3817,7 +3811,7 @@ extension SignalServiceProtos_SyncMessage.Read: SwiftProtobuf.Message, SwiftProt
extension SignalServiceProtos_SyncMessage.Configuration: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = SignalServiceProtos_SyncMessage.protoMessageName + ".Configuration"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "readReceipts"),
1: .same(proto: "readReceipts")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -3855,7 +3849,7 @@ extension SignalServiceProtos_AttachmentPointer: SwiftProtobuf.Message, SwiftPro
7: .same(proto: "fileName"),
8: .same(proto: "flags"),
9: .same(proto: "width"),
10: .same(proto: "height"),
10: .same(proto: "height")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -3928,7 +3922,7 @@ extension SignalServiceProtos_AttachmentPointer: SwiftProtobuf.Message, SwiftPro
extension SignalServiceProtos_AttachmentPointer.Flags: SwiftProtobuf._ProtoNameProviding {
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "VOICE_MESSAGE"),
1: .same(proto: "VOICE_MESSAGE")
]
}
@ -3939,15 +3933,15 @@ extension SignalServiceProtos_GroupContext: SwiftProtobuf.Message, SwiftProtobuf
2: .same(proto: "type"),
3: .same(proto: "name"),
4: .same(proto: "members"),
5: .same(proto: "avatar"),
5: .same(proto: "avatar")
]
fileprivate class _StorageClass {
var _id: Data? = nil
var _type: SignalServiceProtos_GroupContext.TypeEnum? = nil
var _name: String? = nil
var _id: Data?
var _type: SignalServiceProtos_GroupContext.TypeEnum?
var _name: String?
var _members: [String] = []
var _avatar: SignalServiceProtos_AttachmentPointer? = nil
var _avatar: SignalServiceProtos_AttachmentPointer?
static let defaultInstance = _StorageClass()
@ -4031,7 +4025,7 @@ extension SignalServiceProtos_GroupContext.TypeEnum: SwiftProtobuf._ProtoNamePro
1: .same(proto: "UPDATE"),
2: .same(proto: "DELIVER"),
3: .same(proto: "QUIT"),
4: .same(proto: "REQUEST_INFO"),
4: .same(proto: "REQUEST_INFO")
]
}
@ -4045,18 +4039,18 @@ extension SignalServiceProtos_ContactDetails: SwiftProtobuf.Message, SwiftProtob
5: .same(proto: "verified"),
6: .same(proto: "profileKey"),
7: .same(proto: "blocked"),
8: .same(proto: "expireTimer"),
8: .same(proto: "expireTimer")
]
fileprivate class _StorageClass {
var _number: String? = nil
var _name: String? = nil
var _avatar: SignalServiceProtos_ContactDetails.Avatar? = nil
var _color: String? = nil
var _verified: SignalServiceProtos_Verified? = nil
var _profileKey: Data? = nil
var _blocked: Bool? = nil
var _expireTimer: UInt32? = nil
var _number: String?
var _name: String?
var _avatar: SignalServiceProtos_ContactDetails.Avatar?
var _color: String?
var _verified: SignalServiceProtos_Verified?
var _profileKey: Data?
var _blocked: Bool?
var _expireTimer: UInt32?
static let defaultInstance = _StorageClass()
@ -4156,7 +4150,7 @@ extension SignalServiceProtos_ContactDetails.Avatar: SwiftProtobuf.Message, Swif
static let protoMessageName: String = SignalServiceProtos_ContactDetails.protoMessageName + ".Avatar"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "contentType"),
2: .same(proto: "length"),
2: .same(proto: "length")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -4197,18 +4191,18 @@ extension SignalServiceProtos_GroupDetails: SwiftProtobuf.Message, SwiftProtobuf
5: .same(proto: "active"),
6: .same(proto: "expireTimer"),
7: .same(proto: "color"),
8: .same(proto: "blocked"),
8: .same(proto: "blocked")
]
fileprivate class _StorageClass {
var _id: Data? = nil
var _name: String? = nil
var _id: Data?
var _name: String?
var _members: [String] = []
var _avatar: SignalServiceProtos_GroupDetails.Avatar? = nil
var _active: Bool? = nil
var _expireTimer: UInt32? = nil
var _color: String? = nil
var _blocked: Bool? = nil
var _avatar: SignalServiceProtos_GroupDetails.Avatar?
var _active: Bool?
var _expireTimer: UInt32?
var _color: String?
var _blocked: Bool?
static let defaultInstance = _StorageClass()
@ -4308,7 +4302,7 @@ extension SignalServiceProtos_GroupDetails.Avatar: SwiftProtobuf.Message, SwiftP
static let protoMessageName: String = SignalServiceProtos_GroupDetails.protoMessageName + ".Avatar"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "contentType"),
2: .same(proto: "length"),
2: .same(proto: "length")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {

@ -6,11 +6,12 @@
#define TextSecureKit_Constants_h
typedef NS_ENUM(NSInteger, TSWhisperMessageType) {
TSUnknownMessageType = 0,
TSEncryptedWhisperMessageType = 1,
TSUnknownMessageType = 0,
TSEncryptedWhisperMessageType = 1,
TSIgnoreOnIOSWhisperMessageType = 2, // on droid this is the prekey bundle message irrelevant for us
TSPreKeyWhisperMessageType = 3,
TSPreKeyWhisperMessageType = 3,
TSUnencryptedWhisperMessageType = 4,
TSUnidentifiedSenderMessageType = 6,
};
#pragma mark Server Address

@ -1,62 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
#if DEBUG
@objc
public class OWSFakeUDManager: NSObject, OWSUDManager {
@objc public func setup() {}
// MARK: - Recipient state
private var udRecipientSet = Set<String>()
@objc
public func isUDRecipientId(_ recipientId: String) -> Bool {
return udRecipientSet.contains(recipientId)
}
@objc
public func addUDRecipientId(_ recipientId: String) {
udRecipientSet.insert(recipientId)
}
@objc
public func removeUDRecipientId(_ recipientId: String) {
udRecipientSet.remove(recipientId)
}
// MARK: - Server Certificate
// Tests can control the behavior of this mock by setting this property.
@objc public var nextSenderCertificate: Data?
@objc public func ensureSenderCertificateObjC(success:@escaping (Data) -> Void,
failure:@escaping (Error) -> Void) {
guard let certificateData = nextSenderCertificate else {
failure(OWSUDError.assertionError(description: "No mock server certificate data"))
return
}
success(certificateData)
}
// MARK: - Unrestricted Access
private var _shouldAllowUnrestrictedAccess = false
@objc
public func shouldAllowUnrestrictedAccess() -> Bool {
return _shouldAllowUnrestrictedAccess
}
@objc
public func setShouldAllowUnrestrictedAccess(_ value: Bool) {
_shouldAllowUnrestrictedAccess = value
}
}
#endif
Loading…
Cancel
Save