Merge branch 'charlesmchen/unrestrictedRandomProfileGets' into release/2.31.0

pull/1/head
Matthew Chen 7 years ago
commit c556d0c722

@ -1 +1 @@
Subproject commit 17be5b4abbf7aa6acb63755be86bbb17cd072b78
Subproject commit 3b101143e57b34fbef613c365c9ee2e29bd25d16

@ -5,6 +5,7 @@
import Foundation
import PromiseKit
import SignalServiceKit
import SignalMetadataKit
@objc
public class ProfileFetcherJob: NSObject {
@ -134,16 +135,66 @@ public class ProfileFetcherJob: NSObject {
Logger.error("getProfile: \(recipientId)")
let unidentifiedAccess: SSKUnidentifiedAccess? = self.getUnidentifiedAccess(forRecipientId: recipientId)
let requestMaker = RequestMaker(requestFactoryBlock: { (unidentifiedAccessForRequest) -> TSRequest in
return OWSRequestFactory.getProfileRequest(recipientId: recipientId, unidentifiedAccess: unidentifiedAccessForRequest)
switch self.udManager.unidentifiedAccessMode(forRecipientId: recipientId) {
case .unknown:
if let udAccessKey = udManager.udAccessKey(forRecipientId: recipientId) {
// If we are in unknown mode and have a profile key,
// try using the profile key.
return self.requestProfile(recipientId: recipientId,
udAccessKey: udAccessKey,
canFailoverUDAuth: true)
} else {
// If we are in unknown mode and don't have a profile key,
// try using a random UD access key in case they support
// unrestricted access.
let randomUDAccessKey = self.udManager.randomUDAccessKey()
return requestProfile(recipientId: recipientId,
udAccessKey: randomUDAccessKey,
canFailoverUDAuth: true)
}
case .unrestricted:
let randomUDAccessKey = self.udManager.randomUDAccessKey()
return requestProfile(recipientId: recipientId,
udAccessKey: randomUDAccessKey,
canFailoverUDAuth: false)
.recover { (_: Error) -> Promise<SignalServiceProfile> in
Logger.verbose("Failing over to non-random access.")
let udAccessKey = self.udManager.udAccessKey(forRecipientId: recipientId)
// This may fail over again to non-UD-auth.
return self.requestProfile(recipientId: recipientId,
udAccessKey: udAccessKey,
canFailoverUDAuth: true)
}
case .disabled:
// This may fail over to non-UD-auth.
return requestProfile(recipientId: recipientId,
udAccessKey: nil,
canFailoverUDAuth: true)
case .enabled:
// This may be nil if we don't have a profile key for them.
let udAccessKey = udManager.udAccessKey(forRecipientId: recipientId)
// This may fail over to non-UD-auth.
return requestProfile(recipientId: recipientId,
udAccessKey: udAccessKey,
canFailoverUDAuth: true)
}
}
private func requestProfile(recipientId: String,
udAccessKey: SMKUDAccessKey?,
canFailoverUDAuth: Bool) -> Promise<SignalServiceProfile> {
AssertIsOnMainThread()
let requestMaker = RequestMaker(label: "Profile Fetch",
requestFactoryBlock: { (udAccessKeyForRequest) -> TSRequest in
return OWSRequestFactory.getProfileRequest(recipientId: recipientId, udAccessKey: udAccessKeyForRequest)
}, udAuthFailureBlock: {
// Do nothing
}, websocketFailureBlock: {
// Do nothing
}, recipientId: recipientId,
unidentifiedAccess: unidentifiedAccess,
canFailoverUDAuth: true)
udAccessKey: udAccessKey,
canFailoverUDAuth: canFailoverUDAuth)
return requestMaker.makeRequest()
.map { (result: RequestMakerResult) -> SignalServiceProfile in
try SignalServiceProfile(recipientId: recipientId, responseObject: result.responseObject)
@ -158,7 +209,9 @@ public class ProfileFetcherJob: NSObject {
profileNameEncrypted: signalServiceProfile.profileNameEncrypted,
avatarUrlPath: signalServiceProfile.avatarUrlPath)
updateUnidentifiedAccess(recipientId: recipientId, verifier: signalServiceProfile.unidentifiedAccessVerifier, hasUnrestrictedAccess: signalServiceProfile.hasUnrestrictedUnidentifiedAccess)
updateUnidentifiedAccess(recipientId: recipientId,
verifier: signalServiceProfile.unidentifiedAccessVerifier,
hasUnrestrictedAccess: signalServiceProfile.hasUnrestrictedUnidentifiedAccess)
}
private func updateUnidentifiedAccess(recipientId: String, verifier: Data?, hasUnrestrictedAccess: Bool) {
@ -174,7 +227,7 @@ public class ProfileFetcherJob: NSObject {
return
}
guard let udAccessKey = udManager.rawUDAccessKeyForRecipient(recipientId) else {
guard let udAccessKey = udManager.udAccessKey(forRecipientId: recipientId) else {
udManager.setUnidentifiedAccessMode(.disabled, recipientId: recipientId)
return
}
@ -205,8 +258,4 @@ public class ProfileFetcherJob: NSObject {
}
}
}
private func getUnidentifiedAccess(forRecipientId recipientId: RecipientIdentifier) -> SSKUnidentifiedAccess? {
return self.udManager.getAccess(forRecipientId: recipientId)?.targetUnidentifiedAccess
}
}

@ -32,7 +32,10 @@ public class OWSMessageSend: NSObject {
public var hasWebsocketSendFailed = false
@objc
public var unidentifiedAccess: SSKUnidentifiedAccess?
public var udAccessKey: SMKUDAccessKey?
@objc
public var senderCertificate: SMKSenderCertificate?
@objc
public let localNumber: String
@ -51,7 +54,7 @@ public class OWSMessageSend: NSObject {
thread: TSThread?,
recipient: SignalRecipient,
senderCertificate: SMKSenderCertificate?,
udManager: OWSUDManager,
udAccessKey: SMKUDAccessKey?,
localNumber: String,
success: @escaping () -> Void,
failure: @escaping (Error) -> Void) {
@ -59,14 +62,14 @@ public class OWSMessageSend: NSObject {
self.thread = thread
self.recipient = recipient
self.localNumber = localNumber
self.senderCertificate = senderCertificate
self.udAccessKey = udAccessKey
if let recipientId = recipient.uniqueId {
self.unidentifiedAccess = udManager.getAccess(forRecipientId: recipientId)?.targetUnidentifiedAccess
self.isLocalNumber = localNumber == recipientId
} else {
owsFailDebug("SignalRecipient missing recipientId")
self.isLocalNumber = false
self.unidentifiedAccess = nil
}
self.success = success
@ -75,16 +78,18 @@ public class OWSMessageSend: NSObject {
@objc
public var isUDSend: Bool {
return self.unidentifiedAccess != nil
return udAccessKey != nil && senderCertificate != nil
}
@objc
public func disableUD() {
unidentifiedAccess = nil
Logger.verbose("")
udAccessKey = nil
}
@objc
public func setHasUDAuthFailed() {
Logger.verbose("")
// We "fail over" to non-UD sends after auth errors sending via UD.
disableUD()
}

@ -578,6 +578,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
message:(TSOutgoingMessage *)message
thread:(nullable TSThread *)thread
senderCertificate:(nullable SMKSenderCertificate *)senderCertificate
selfUDAccessKey:(nullable SMKUDAccessKey *)selfUDAccessKey
sendErrors:(NSMutableArray<NSError *> *)sendErrors
{
OWSAssertDebug(recipients.count > 0);
@ -589,11 +590,16 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
for (SignalRecipient *recipient in recipients) {
// Use chained promises to make the code more readable.
AnyPromise *sendPromise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
SMKUDAccessKey *_Nullable theirUDAccessKey;
if (senderCertificate != nil && selfUDAccessKey != nil) {
theirUDAccessKey = [self.udManager udSendAccessKeyForRecipientId:recipient.recipientId];
}
OWSMessageSend *messageSend = [[OWSMessageSend alloc] initWithMessage:message
thread:thread
recipient:recipient
senderCertificate:senderCertificate
udManager:self.udManager
udAccessKey:theirUDAccessKey
localNumber:self.tsAccountManager.localNumber
success:^{
// The value doesn't matter, we just need any non-NSError value.
@ -624,10 +630,16 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
{
AssertIsOnSendingQueue();
SMKUDAccessKey *_Nullable selfUDAccessKey;
if (senderCertificate) {
selfUDAccessKey = [self.udManager udSendAccessKeyForRecipientId:self.tsAccountManager.localNumber];
}
void (^successHandler)(void) = ^() {
dispatch_async([OWSDispatch sendingQueue], ^{
[self handleMessageSentLocally:message
senderCertificate:senderCertificate
selfUDAccessKey:selfUDAccessKey
success:^{
successHandlerParam();
}
@ -645,6 +657,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
dispatch_async([OWSDispatch sendingQueue], ^{
[self handleMessageSentLocally:message
senderCertificate:senderCertificate
selfUDAccessKey:selfUDAccessKey
success:^{
failureHandlerParam(error);
}
@ -726,6 +739,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
message:message
thread:thread
senderCertificate:senderCertificate
selfUDAccessKey:selfUDAccessKey
sendErrors:sendErrors]
.then(^(id value) {
successHandler();
@ -1061,16 +1075,14 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
OWSLogWarn(@"Sending a message with no device messages.");
}
OWSLogVerbose(@"Sending message; ud? %d %d.", messageSend.isUDSend, messageSend.unidentifiedAccess != nil);
// NOTE: canFailoverUDAuth is NO because UD-auth and Non-UD-auth requests
// use different device lists.
OWSRequestMaker *requestMaker = [[OWSRequestMaker alloc]
initWithRequestFactoryBlock:^(SSKUnidentifiedAccess *_Nullable unidentifiedAccess) {
OWSRequestMaker *requestMaker = [[OWSRequestMaker alloc] initWithLabel:@"Message Send"
requestFactoryBlock:^(SMKUDAccessKey *_Nullable udAccessKey) {
return [OWSRequestFactory submitMessageRequestWithRecipient:recipient.recipientId
messages:deviceMessages
timeStamp:message.timestamp
unidentifiedAccess:unidentifiedAccess];
udAccessKey:udAccessKey];
}
udAuthFailureBlock:^{
[messageSend setHasUDAuthFailed];
@ -1079,7 +1091,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
messageSend.hasWebsocketSendFailed = YES;
}
recipientId:recipient.recipientId
unidentifiedAccess:messageSend.unidentifiedAccess
udAccessKey:messageSend.udAccessKey
canFailoverUDAuth:NO];
[[requestMaker makeRequestObjc]
.then(^(OWSRequestMakerResult *result) {
@ -1328,6 +1340,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
- (void)handleMessageSentLocally:(TSOutgoingMessage *)message
senderCertificate:(nullable SMKSenderCertificate *)senderCertificate
selfUDAccessKey:(nullable SMKUDAccessKey *)selfUDAccessKey
success:(void (^)(void))success
failure:(RetryableFailureHandler)failure
{
@ -1344,6 +1357,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
[self
sendSyncTranscriptForMessage:message
senderCertificate:senderCertificate
selfUDAccessKey:selfUDAccessKey
success:^{
// TODO: We might send to a recipient, then to another recipient on retry.
// To ensure desktop receives all "delivery status" info, we might
@ -1360,6 +1374,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
- (void)sendSyncTranscriptForMessage:(TSOutgoingMessage *)message
senderCertificate:(nullable SMKSenderCertificate *)senderCertificate
selfUDAccessKey:(nullable SMKUDAccessKey *)selfUDAccessKey
success:(void (^)(void))success
failure:(RetryableFailureHandler)failure
{
@ -1376,7 +1391,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
thread:message.thread
recipient:recipient
senderCertificate:senderCertificate
udManager:self.udManager
udAccessKey:selfUDAccessKey
localNumber:self.tsAccountManager.localNumber
success:^{
OWSLogInfo(@"Successfully sent sync transcript.");
@ -1558,11 +1573,11 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
NSString *recipientId = recipient.recipientId;
OWSAssertDebug(recipientId.length > 0);
OWSRequestMaker *requestMaker = [[OWSRequestMaker alloc]
initWithRequestFactoryBlock:^(SSKUnidentifiedAccess *_Nullable unidentifiedAccess) {
OWSRequestMaker *requestMaker = [[OWSRequestMaker alloc] initWithLabel:@"Prekey Fetch"
requestFactoryBlock:^(SMKUDAccessKey *_Nullable udAccessKey) {
return [OWSRequestFactory recipientPrekeyRequestWithRecipient:recipientId
deviceId:[deviceId stringValue]
unidentifiedAccess:unidentifiedAccess];
udAccessKey:udAccessKey];
}
udAuthFailureBlock:^{
[messageSend setHasUDAuthFailed];
@ -1571,7 +1586,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
messageSend.hasWebsocketSendFailed = YES;
}
recipientId:recipientId
unidentifiedAccess:messageSend.unidentifiedAccess
udAccessKey:messageSend.udAccessKey
canFailoverUDAuth:YES];
[[requestMaker makeRequestObjc]
.then(^(OWSRequestMakerResult *result) {
@ -1646,7 +1661,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
serializedMessage = [secretCipher encryptMessageWithRecipientId:recipientId
deviceId:deviceId.intValue
paddedPlaintext:[plainText paddedMessageBody]
senderCertificate:messageSend.unidentifiedAccess.senderCertificate
senderCertificate:messageSend.senderCertificate
protocolContext:transaction
error:&error];
messageType = TSUnidentifiedSenderMessageType;

@ -4,6 +4,7 @@
import Foundation
import PromiseKit
import SignalMetadataKit
@objc
public enum RequestMakerUDAuthError: Int, Error {
@ -36,29 +37,33 @@ public class RequestMakerResult: NSObject {
// * Websocket-to-REST failover.
@objc(OWSRequestMaker)
public class RequestMaker: NSObject {
public typealias RequestFactoryBlock = (SSKUnidentifiedAccess?) -> TSRequest
public typealias RequestFactoryBlock = (SMKUDAccessKey?) -> TSRequest
public typealias UDAuthFailureBlock = () -> Void
public typealias WebsocketFailureBlock = () -> Void
private let label: String
private let requestFactoryBlock: RequestFactoryBlock
private let udAuthFailureBlock: UDAuthFailureBlock
private let websocketFailureBlock: WebsocketFailureBlock
private let recipientId: String
private let unidentifiedAccess: SSKUnidentifiedAccess?
private let udAccessKey: SMKUDAccessKey?
private let canFailoverUDAuth: Bool
@objc
public init(requestFactoryBlock : @escaping RequestFactoryBlock,
public init(label: String,
requestFactoryBlock : @escaping RequestFactoryBlock,
udAuthFailureBlock : @escaping UDAuthFailureBlock,
websocketFailureBlock : @escaping WebsocketFailureBlock,
recipientId: String,
unidentifiedAccess: SSKUnidentifiedAccess?,
udAccessKey: SMKUDAccessKey?,
canFailoverUDAuth: Bool) {
self.label = label
self.requestFactoryBlock = requestFactoryBlock
self.udAuthFailureBlock = udAuthFailureBlock
self.websocketFailureBlock = websocketFailureBlock
self.recipientId = recipientId
self.unidentifiedAccess = unidentifiedAccess
self.udAccessKey = udAccessKey
self.canFailoverUDAuth = canFailoverUDAuth
}
@ -99,18 +104,26 @@ public class RequestMaker: NSObject {
}
private func makeRequestInternal(skipUD: Bool, skipWebsocket: Bool) -> Promise<RequestMakerResult> {
var unidentifiedAccessForRequest: SSKUnidentifiedAccess?
var udAccessKeyForRequest: SMKUDAccessKey?
if !skipUD {
unidentifiedAccessForRequest = unidentifiedAccess
udAccessKeyForRequest = udAccessKey
}
let isUDSend = unidentifiedAccessForRequest != nil
let request = requestFactoryBlock(unidentifiedAccessForRequest)
let isUDSend = udAccessKeyForRequest != nil
let request = requestFactoryBlock(udAccessKeyForRequest)
let webSocketType: OWSWebSocketType = (isUDSend ? .UD : .default)
let canMakeWebsocketRequests = (socketManager.canMakeRequests(of: webSocketType) && !skipWebsocket)
if canMakeWebsocketRequests {
return Promise { resolver in
socketManager.make(request, webSocketType: webSocketType, success: { (responseObject: Any?) in
if self.udManager.isUDVerboseLoggingEnabled() {
if isUDSend {
Logger.debug("UD websocket request '\(self.label)' succeeded.")
} else {
Logger.debug("Non-UD websocket request '\(self.label)' succeeded.")
}
}
resolver.fulfill(RequestMakerResult(responseObject: responseObject, wasSentByUD: isUDSend))
}) { (statusCode: Int, responseData: Data?, error: Error) in
resolver.reject(RequestMakerError.websocketRequestError(statusCode: statusCode, responseData: responseData, underlyingError: error))
@ -124,10 +137,10 @@ public class RequestMaker: NSObject {
self.udManager.setUnidentifiedAccessMode(.disabled, recipientId: self.recipientId)
self.udAuthFailureBlock()
if self.canFailoverUDAuth {
Logger.info("UD websocket request auth failed; failing over to non-UD websocket request.")
Logger.info("UD websocket request '\(self.label)' auth failed; failing over to non-UD websocket request.")
return self.makeRequestInternal(skipUD: true, skipWebsocket: skipWebsocket)
} else {
Logger.info("UD websocket request auth failed; aborting.")
Logger.info("UD websocket request '\(self.label)' auth failed; aborting.")
throw RequestMakerUDAuthError.udAuthFailure
}
}
@ -137,14 +150,22 @@ public class RequestMaker: NSObject {
}
self.websocketFailureBlock()
Logger.info("Non-UD Web socket request failed; failing over to REST request: \(error).")
Logger.info("Non-UD Web socket request '\(self.label)' failed; failing over to REST request: \(error).")
return self.makeRequestInternal(skipUD: skipUD, skipWebsocket: true)
}
} else {
return self.networkManager.makePromise(request: request)
.map { (networkManagerResult: TSNetworkManager.NetworkManagerResult) -> RequestMakerResult in
if self.udManager.isUDVerboseLoggingEnabled() {
if isUDSend {
Logger.debug("UD REST request '\(self.label)' succeeded.")
} else {
Logger.debug("Non-UD REST request '\(self.label)' succeeded.")
}
}
// Unwrap the network manager promise into a request maker promise.
RequestMakerResult(responseObject: networkManagerResult.responseObject, wasSentByUD: isUDSend)
return RequestMakerResult(responseObject: networkManagerResult.responseObject, wasSentByUD: isUDSend)
}.recover { (error: Error) -> Promise<RequestMakerResult> in
switch error {
case NetworkManagerError.taskError(let task, _):
@ -155,10 +176,10 @@ public class RequestMaker: NSObject {
self.udManager.setUnidentifiedAccessMode(.disabled, recipientId: self.recipientId)
self.udAuthFailureBlock()
if self.canFailoverUDAuth {
Logger.info("UD REST request auth failed; failing over to non-UD REST request.")
Logger.info("UD REST request '\(self.label)' auth failed; failing over to non-UD REST request.")
return self.makeRequestInternal(skipUD: true, skipWebsocket: skipWebsocket)
} else {
Logger.info("UD REST request auth failed; aborting.")
Logger.info("UD REST request '\(self.label)' auth failed; aborting.")
throw RequestMakerUDAuthError.udAuthFailure
}
}
@ -167,7 +188,7 @@ public class RequestMaker: NSObject {
break
}
Logger.debug("Non-UD REST request failed: \(error).")
Logger.debug("Non-UD REST request '\(self.label)' failed: \(error).")
throw error
}
}

@ -20,6 +20,19 @@ public enum UnidentifiedAccessMode: Int {
case unrestricted
}
private func string(forUnidentifiedAccessMode mode: UnidentifiedAccessMode) -> String {
switch mode {
case .unknown:
return "unknown"
case .enabled:
return "enabled"
case .disabled:
return "disabled"
case .unrestricted:
return "unrestricted"
}
}
@objc public protocol OWSUDManager: class {
@objc func setup()
@ -28,39 +41,39 @@ public enum UnidentifiedAccessMode: Int {
@objc func isUDEnabled() -> Bool
@objc func isUDVerboseLoggingEnabled() -> Bool
// MARK: - Recipient State
@objc
func setUnidentifiedAccessMode(_ mode: UnidentifiedAccessMode, recipientId: String)
@objc
func getAccess(forRecipientId recipientId: RecipientIdentifier) -> SSKUnidentifiedAccessPair?
func randomUDAccessKey() -> SMKUDAccessKey
// Returns the UD access key for a given recipient if:
//
// * UD is enabled.
// * Their UD mode is enabled or unrestricted.
// * We have a valid profile key for them.
@objc func enabledUDAccessKeyForRecipient(_ recipientId: RecipientIdentifier) -> SMKUDAccessKey?
@objc
func unidentifiedAccessMode(forRecipientId recipientId: RecipientIdentifier) -> UnidentifiedAccessMode
// Returns the UD access key for a given recipient if:
//
// * We have a valid profile key for them.
@objc func rawUDAccessKeyForRecipient(_ recipientId: RecipientIdentifier) -> SMKUDAccessKey?
@objc
func udAccessKey(forRecipientId recipientId: RecipientIdentifier) -> SMKUDAccessKey?
// MARK: - Local State
@objc
func udSendAccessKey(forRecipientId recipientId: RecipientIdentifier) -> SMKUDAccessKey?
// MARK: Sender Certificate
// We use completion handlers instead of a promise so that message sending
// logic can access the strongly typed certificate data.
@objc func ensureSenderCertificateObjC(success:@escaping (SMKSenderCertificate) -> Void,
@objc
func ensureSenderCertificateObjC(success:@escaping (SMKSenderCertificate) -> Void,
failure:@escaping (Error) -> Void)
// MARK: Unrestricted Access
@objc func shouldAllowUnrestrictedAccessLocal() -> Bool
@objc func setShouldAllowUnrestrictedAccessLocal(_ value: Bool)
@objc
func shouldAllowUnrestrictedAccessLocal() -> Bool
@objc
func setShouldAllowUnrestrictedAccessLocal(_ value: Bool)
}
// MARK: -
@ -111,6 +124,24 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
ensureSenderCertificate().retainUntilComplete()
}
// MARK: -
@objc
public func isUDEnabled() -> Bool {
// Only enable UD if UD is supported by all linked devices,
// so that sync messages can also be sent via UD.
guard let localNumber = tsAccountManager.localNumber() else {
return false
}
let ourAccessMode = unidentifiedAccessMode(forRecipientId: localNumber)
return ourAccessMode == .enabled || ourAccessMode == .unrestricted
}
@objc
public func isUDVerboseLoggingEnabled() -> Bool {
return true
}
// MARK: - Dependencies
private var profileManager: ProfileManagerProtocol {
@ -124,44 +155,13 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
// MARK: - Recipient state
@objc
public func getAccess(forRecipientId recipientId: RecipientIdentifier) -> SSKUnidentifiedAccessPair? {
let theirAccessMode = unidentifiedAccessMode(recipientId: recipientId)
guard theirAccessMode == .enabled || theirAccessMode == .unrestricted else {
return nil
}
guard let theirAccessKey = enabledUDAccessKeyForRecipient(recipientId) else {
return nil
}
guard let ourSenderCertificate = senderCertificate() else {
return nil
}
guard let ourAccessKey: SMKUDAccessKey = {
if shouldAllowUnrestrictedAccessLocal() {
return SMKUDAccessKey(randomKeyData: ())
} else {
guard let localNumber = tsAccountManager.localNumber() else {
owsFailDebug("localNumber was unexpectedly nil")
return nil
}
return enabledUDAccessKeyForRecipient(localNumber)
}
}() else {
return nil
}
let targetUnidentifiedAccess = SSKUnidentifiedAccess(accessKey: theirAccessKey, senderCertificate: ourSenderCertificate)
let selfUnidentifiedAccess = SSKUnidentifiedAccess(accessKey: ourAccessKey, senderCertificate: ourSenderCertificate)
return SSKUnidentifiedAccessPair(targetUnidentifiedAccess: targetUnidentifiedAccess,
selfUnidentifiedAccess: selfUnidentifiedAccess)
public func randomUDAccessKey() -> SMKUDAccessKey {
return SMKUDAccessKey(randomKeyData: ())
}
@objc
func unidentifiedAccessMode(recipientId: RecipientIdentifier) -> UnidentifiedAccessMode {
guard let existingRawValue = dbConnection.object(forKey: recipientId, inCollection: kUnidentifiedAccessCollection) as? Int else {
private func unidentifiedAccessMode(forRecipientId recipientId: RecipientIdentifier,
transaction: YapDatabaseReadTransaction) -> UnidentifiedAccessMode {
guard let existingRawValue = transaction.object(forKey: recipientId, inCollection: kUnidentifiedAccessCollection) as? Int else {
return .unknown
}
guard let existingValue = UnidentifiedAccessMode(rawValue: existingRawValue) else {
@ -170,35 +170,38 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
return existingValue
}
@objc
public func unidentifiedAccessMode(forRecipientId recipientId: RecipientIdentifier) -> UnidentifiedAccessMode {
var mode: UnidentifiedAccessMode = .unknown
dbConnection.read { (transaction) in
mode = self.unidentifiedAccessMode(forRecipientId: recipientId, transaction: transaction)
}
return mode
}
@objc
public func setUnidentifiedAccessMode(_ mode: UnidentifiedAccessMode, recipientId: String) {
if let localNumber = tsAccountManager.localNumber() {
if recipientId == localNumber {
Logger.info("Setting local UD access mode: \(mode.rawValue)")
Logger.info("Setting local UD access mode: \(string(forUnidentifiedAccessMode: mode))")
}
}
dbConnection.setObject(mode.rawValue as Int, forKey: recipientId, inCollection: kUnidentifiedAccessCollection)
}
dbConnection.readWrite { (transaction) in
let oldMode = self.unidentifiedAccessMode(forRecipientId: recipientId, transaction: transaction)
// Returns the UD access key for a given recipient
// if we have a valid profile key for them.
@objc
public func enabledUDAccessKeyForRecipient(_ recipientId: RecipientIdentifier) -> SMKUDAccessKey? {
guard isUDEnabled() else {
return nil
}
let theirAccessMode = unidentifiedAccessMode(recipientId: recipientId)
if theirAccessMode == .unrestricted {
return SMKUDAccessKey(randomKeyData: ())
transaction.setObject(mode.rawValue as Int, forKey: recipientId, inCollection: self.kUnidentifiedAccessCollection)
if mode != oldMode {
Logger.info("Setting UD access mode for \(recipientId): \(string(forUnidentifiedAccessMode: oldMode)) -> \(string(forUnidentifiedAccessMode: mode))")
}
}
return rawUDAccessKeyForRecipient(recipientId)
}
// Returns the UD access key for a given recipient
// if we have a valid profile key for them.
@objc
public func rawUDAccessKeyForRecipient(_ recipientId: RecipientIdentifier) -> SMKUDAccessKey? {
public func udAccessKey(forRecipientId recipientId: RecipientIdentifier) -> SMKUDAccessKey? {
guard let profileKey = profileManager.profileKeyData(forRecipientId: recipientId) else {
// Mark as "not a UD recipient".
return nil
@ -212,6 +215,50 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
}
}
// Returns the UD access key for sending to a given recipient.
@objc
public func udSendAccessKey(forRecipientId recipientId: RecipientIdentifier) -> SMKUDAccessKey? {
// This check is currently redundant with the "send access key for local number"
// check below, but behavior of isUDEnabled() may change.
guard isUDEnabled() else {
if isUDVerboseLoggingEnabled() {
Logger.info("UD Send disabled for \(recipientId), UD disabled.")
}
return nil
}
guard let localNumber = tsAccountManager.localNumber() else {
if isUDVerboseLoggingEnabled() {
Logger.info("UD Send disabled for \(recipientId), no local number.")
}
return nil
}
if localNumber != recipientId {
guard udSendAccessKey(forRecipientId: localNumber) != nil else {
if isUDVerboseLoggingEnabled() {
Logger.info("UD Send disabled for \(recipientId), UD disabled for sync messages.")
}
return nil
}
}
let accessMode = unidentifiedAccessMode(forRecipientId: localNumber)
if accessMode == .unrestricted {
if isUDVerboseLoggingEnabled() {
Logger.info("UD Send enabled for \(recipientId) with random key.")
}
return randomUDAccessKey()
}
guard accessMode == .enabled else {
if isUDVerboseLoggingEnabled() {
Logger.info("UD Send disabled for \(recipientId), UD not enabled for this recipient.")
}
return nil
}
if isUDVerboseLoggingEnabled() {
Logger.info("UD Send enabled for \(recipientId).")
}
return udAccessKey(forRecipientId: recipientId)
}
// MARK: - Sender Certificate
#if DEBUG
@ -309,17 +356,6 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
}
}
@objc
public func isUDEnabled() -> Bool {
// Only enable UD if UD is supported by all linked devices,
// so that sync messages can also be sent via UD.
guard let localNumber = tsAccountManager.localNumber() else {
return false
}
let ourAccessMode = unidentifiedAccessMode(recipientId: localNumber)
return ourAccessMode == .enabled || ourAccessMode == .unrestricted
}
@objc
public func trustRoot() -> ECPublicKey {
return OWSUDManagerImpl.trustRoot()

@ -7,7 +7,7 @@ NS_ASSUME_NONNULL_BEGIN
@class ECKeyPair;
@class OWSDevice;
@class PreKeyRecord;
@class SSKUnidentifiedAccess;
@class SMKUDAccessKey;
@class SignedPreKeyRecord;
@class TSRequest;
@ -37,8 +37,8 @@ typedef NS_ENUM(NSUInteger, TSVerificationTransport) { TSVerificationTransportVo
+ (TSRequest *)getMessagesRequest;
+ (TSRequest *)getProfileRequestWithRecipientId:(NSString *)recipientId
unidentifiedAccess:(nullable SSKUnidentifiedAccess *)unidentifiedAccess
NS_SWIFT_NAME(getProfileRequest(recipientId:unidentifiedAccess:));
udAccessKey:(nullable SMKUDAccessKey *)udAccessKey
NS_SWIFT_NAME(getProfileRequest(recipientId:udAccessKey:));
+ (TSRequest *)turnServerInfoRequest;
@ -62,7 +62,7 @@ typedef NS_ENUM(NSUInteger, TSVerificationTransport) { TSVerificationTransportVo
+ (TSRequest *)submitMessageRequestWithRecipient:(NSString *)recipientId
messages:(NSArray *)messages
timeStamp:(uint64_t)timeStamp
unidentifiedAccess:(nullable SSKUnidentifiedAccess *)unidentifiedAccess;
udAccessKey:(nullable SMKUDAccessKey *)udAccessKey;
+ (TSRequest *)verifyCodeRequestWithVerificationCode:(NSString *)verificationCode
forNumber:(NSString *)phoneNumber
@ -78,7 +78,7 @@ typedef NS_ENUM(NSUInteger, TSVerificationTransport) { TSVerificationTransportVo
+ (TSRequest *)recipientPrekeyRequestWithRecipient:(NSString *)recipientNumber
deviceId:(NSString *)deviceId
unidentifiedAccess:(nullable SSKUnidentifiedAccess *)unidentifiedAccess;
udAccessKey:(nullable SMKUDAccessKey *)udAccessKey;
+ (TSRequest *)registerSignedPrekeyRequestWithSignedPreKeyRecord:(SignedPreKeyRecord *)signedPreKey;

@ -124,14 +124,14 @@ NS_ASSUME_NONNULL_BEGIN
}
+ (TSRequest *)getProfileRequestWithRecipientId:(NSString *)recipientId
unidentifiedAccess:(nullable SSKUnidentifiedAccess *)unidentifiedAccess
udAccessKey:(nullable SMKUDAccessKey *)udAccessKey
{
OWSAssertDebug(recipientId.length > 0);
NSString *path = [NSString stringWithFormat:textSecureProfileAPIFormat, recipientId];
TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}];
if (unidentifiedAccess != nil) {
[self useUDAuthWithRequest:request accessKey:unidentifiedAccess.accessKey];
if (udAccessKey != nil) {
[self useUDAuthWithRequest:request accessKey:udAccessKey];
}
return request;
}
@ -188,7 +188,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (TSRequest *)recipientPrekeyRequestWithRecipient:(NSString *)recipientNumber
deviceId:(NSString *)deviceId
unidentifiedAccess:(nullable SSKUnidentifiedAccess *)unidentifiedAccess;
udAccessKey:(nullable SMKUDAccessKey *)udAccessKey
{
OWSAssertDebug(recipientNumber.length > 0);
OWSAssertDebug(deviceId.length > 0);
@ -196,8 +196,8 @@ NS_ASSUME_NONNULL_BEGIN
NSString *path = [NSString stringWithFormat:@"%@/%@/%@", textSecureKeysAPI, recipientNumber, deviceId];
TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}];
if (unidentifiedAccess != nil) {
[self useUDAuthWithRequest:request accessKey:unidentifiedAccess.accessKey];
if (udAccessKey != nil) {
[self useUDAuthWithRequest:request accessKey:udAccessKey];
}
return request;
}
@ -328,7 +328,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (TSRequest *)submitMessageRequestWithRecipient:(NSString *)recipientId
messages:(NSArray *)messages
timeStamp:(uint64_t)timeStamp
unidentifiedAccess:(nullable SSKUnidentifiedAccess *)unidentifiedAccess
udAccessKey:(nullable SMKUDAccessKey *)udAccessKey
{
// NOTE: messages may be empty; See comments in OWSDeviceManager.
OWSAssertDebug(recipientId.length > 0);
@ -341,8 +341,8 @@ NS_ASSUME_NONNULL_BEGIN
};
TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"PUT" parameters:parameters];
if (unidentifiedAccess != nil) {
[self useUDAuthWithRequest:request accessKey:unidentifiedAccess.accessKey];
if (udAccessKey != nil) {
[self useUDAuthWithRequest:request accessKey:udAccessKey];
}
return request;
}

@ -19,7 +19,6 @@ public protocol SignalServiceClient: SignalServiceClientObjC {
func setCurrentSignedPreKey(_ signedPreKey: SignedPreKeyRecord) -> Promise<Void>
func requestUDSenderCertificate() -> Promise<Data>
func updateAccountAttributes() -> Promise<Void>
func retrieveProfile(recipientId: RecipientIdentifier, unidentifiedAccess: SSKUnidentifiedAccess?) -> Promise<SignalServiceProfile>
}
/// Based on libsignal-service-java's PushServiceSocket class
@ -92,27 +91,4 @@ public class SignalServiceRestClient: NSObject, SignalServiceClient {
let request = OWSRequestFactory.updateAttributesRequest()
return networkManager.makePromise(request: request).asVoid()
}
public func retrieveProfile(recipientId: RecipientIdentifier, unidentifiedAccess: SSKUnidentifiedAccess?) -> Promise<SignalServiceProfile> {
let request = OWSRequestFactory.getProfileRequest(recipientId: recipientId, unidentifiedAccess: unidentifiedAccess)
return networkManager.makePromise(request: request)
.recover { (error: Error) -> Promise<(task: URLSessionDataTask, responseObject: Any?)> in
switch error {
case NetworkManagerError.taskError(let task, _):
let statusCode = task.statusCode()
if unidentifiedAccess != nil && (statusCode == 401 || statusCode == 403) {
Logger.verbose("REST profile request failing over to non-UD auth.")
self.udManager.setUnidentifiedAccessMode(.disabled, recipientId: recipientId)
let nonUDRequest = OWSRequestFactory.getProfileRequest(recipientId: recipientId, unidentifiedAccess: nil)
return self.networkManager.makePromise(request: nonUDRequest)
}
default: break
}
throw error
}.map { _, responseObject in
return try SignalServiceProfile(recipientId: recipientId, responseObject: responseObject)
}
}
}

@ -1,31 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
import SignalMetadataKit
@objc
public class SSKUnidentifiedAccessPair: NSObject {
public let targetUnidentifiedAccess: SSKUnidentifiedAccess
public let selfUnidentifiedAccess: SSKUnidentifiedAccess
init(targetUnidentifiedAccess: SSKUnidentifiedAccess, selfUnidentifiedAccess: SSKUnidentifiedAccess) {
self.targetUnidentifiedAccess = targetUnidentifiedAccess
self.selfUnidentifiedAccess = selfUnidentifiedAccess
}
}
@objc
public class SSKUnidentifiedAccess: NSObject {
@objc
let accessKey: SMKUDAccessKey
@objc
let senderCertificate: SMKSenderCertificate
init(accessKey: SMKUDAccessKey, senderCertificate: SMKSenderCertificate) {
self.accessKey = accessKey
self.senderCertificate = senderCertificate
}
}
Loading…
Cancel
Save