Merge branch 'mkirk/unidentified-profile-fetch' into private-master

pull/1/head
Michael Kirk 7 years ago
commit 92f1827627

@ -56,6 +56,11 @@ public class ProfileFetcherJob: NSObject {
return SSKEnvironment.shared.identityManager
}
private var signalServiceClient: SignalServiceClient {
// TODO hang on SSKEnvironment
return SignalServiceRestClient()
}
// MARK: -
public func run(recipientIds: [String]) {
@ -88,8 +93,7 @@ public class ProfileFetcherJob: NSObject {
}
enum ProfileFetcherJobError: Error {
case throttled(lastTimeInterval: TimeInterval),
unknownNetworkError
case throttled(lastTimeInterval: TimeInterval)
}
public func updateProfile(recipientId: String, remainingRetries: Int = 3) {
@ -130,14 +134,14 @@ public class ProfileFetcherJob: NSObject {
Logger.error("getProfile: \(recipientId)")
let request = OWSRequestFactory.getProfileRequest(withRecipientId: recipientId)
let (promise, fulfill, reject) = Promise<SignalServiceProfile>.pending()
let unidentifiedAccess: SSKUnidentifiedAccess? = self.getUnidentifiedAccess(forRecipientId: recipientId)
let socketType: OWSWebSocketType = unidentifiedAccess == nil ? .default : .UD
if socketManager.canMakeRequests(of: socketType) {
let request = OWSRequestFactory.getProfileRequest(recipientId: recipientId, unidentifiedAccess: unidentifiedAccess)
let (promise, fulfill, reject) = Promise<SignalServiceProfile>.pending()
// TODO: Use UD socket for some profile gets.
if socketManager.canMakeRequests(of: .default) {
self.socketManager.make(request,
webSocketType: .default,
webSocketType: socketType,
success: { (responseObject: Any?) -> Void in
do {
let profile = try SignalServiceProfile(recipientId: recipientId, responseObject: responseObject)
@ -149,27 +153,10 @@ public class ProfileFetcherJob: NSObject {
failure: { (_: NSInteger, _:Data?, error: Error) in
reject(error)
})
return promise
} else {
self.networkManager.makeRequest(request,
success: { (_: URLSessionDataTask?, responseObject: Any?) -> Void in
do {
let profile = try SignalServiceProfile(recipientId: recipientId, responseObject: responseObject)
fulfill(profile)
} catch {
reject(error)
}
},
failure: { (_: URLSessionDataTask?, error: Error?) in
if let error = error {
reject(error)
}
reject(ProfileFetcherJobError.unknownNetworkError)
})
return self.signalServiceClient.retrieveProfile(recipientId: recipientId, unidentifiedAccess: unidentifiedAccess)
}
return promise
}
private func updateProfile(signalServiceProfile: SignalServiceProfile) {
@ -180,29 +167,34 @@ public class ProfileFetcherJob: NSObject {
profileNameEncrypted: signalServiceProfile.profileNameEncrypted,
avatarUrlPath: signalServiceProfile.avatarUrlPath)
// Recipients should be in "UD delivery mode" IFF:
//
// * Their profile includes a unidentifiedAccessVerifier.
// * The unidentifiedAccessVerifier matches the "expected" value derived
// from their profile key (if any).
//
// Recipients should be in "normal delivery mode" otherwise.
var supportsUnidentifiedDelivery = false
if let unidentifiedAccessVerifier = signalServiceProfile.unidentifiedAccessVerifier,
let udAccessKey = udManager.udAccessKeyForRecipient(recipientId) {
let dataToVerify = Data(count: 32)
if let expectedVerfier = Cryptography.computeSHA256HMAC(dataToVerify, withHMACKey: udAccessKey.keyData) {
supportsUnidentifiedDelivery = expectedVerfier == unidentifiedAccessVerifier
} else {
owsFailDebug("could not verify UD")
}
updateUnidentifiedAccess(recipientId: recipientId, verifier: signalServiceProfile.unidentifiedAccessVerifier, hasUnrestrictedAccess: signalServiceProfile.hasUnrestrictedUnidentifiedAccess)
}
private func updateUnidentifiedAccess(recipientId: String, verifier: Data?, hasUnrestrictedAccess: Bool) {
if hasUnrestrictedAccess {
udManager.setUnidentifiedAccessMode(.unrestricted, recipientId: recipientId)
return
}
// TODO: We may want to only call setSupportsUnidentifiedDelivery if
// supportsUnidentifiedDelivery is true.
udManager.setSupportsUnidentifiedDelivery(supportsUnidentifiedDelivery, recipientId: recipientId)
guard let verifier = verifier, let udAccessKey = udManager.udAccessKeyForRecipient(recipientId) else {
udManager.setUnidentifiedAccessMode(.disabled, recipientId: recipientId)
return
}
let dataToVerify = Data(count: 32)
guard let expectedVerfier = Cryptography.computeSHA256HMAC(dataToVerify, withHMACKey: udAccessKey.keyData) else {
owsFailDebug("could not compute verification")
udManager.setUnidentifiedAccessMode(.disabled, recipientId: recipientId)
return
}
udManager.setShouldAllowUnrestrictedAccess(recipientId: recipientId, shouldAllowUnrestrictedAccess: signalServiceProfile.hasUnrestrictedUnidentifiedAccess)
guard expectedVerfier == verifier else {
Logger.verbose("verifier mismatch, new profile key?")
udManager.setUnidentifiedAccessMode(.disabled, recipientId: recipientId)
return
}
udManager.setUnidentifiedAccessMode(.enabled, recipientId: recipientId)
}
private func verifyIdentityUpToDateAsync(recipientId: String, latestIdentityKey: Data) {
@ -215,46 +207,8 @@ public class ProfileFetcherJob: NSObject {
}
}
}
}
@objc
public class SignalServiceProfile: NSObject {
public enum ValidationError: Error {
case invalid(description: String)
case invalidIdentityKey(description: String)
case invalidProfileName(description: String)
}
public let recipientId: String
public let identityKey: Data
public let profileNameEncrypted: Data?
public let avatarUrlPath: String?
public let unidentifiedAccessVerifier: Data?
public let hasUnrestrictedUnidentifiedAccess: Bool
init(recipientId: String, responseObject: Any?) throws {
self.recipientId = recipientId
guard let params = ParamParser(responseObject: responseObject) else {
throw ValidationError.invalid(description: "invalid response: \(String(describing: responseObject))")
}
let identityKeyWithType = try params.requiredBase64EncodedData(key: "identityKey")
let kIdentityKeyLength = 33
guard identityKeyWithType.count == kIdentityKeyLength else {
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
self.profileNameEncrypted = try params.optionalBase64EncodedData(key: "name")
let avatarUrlPath: String? = try params.optional(key: "avatar")
self.avatarUrlPath = avatarUrlPath
self.unidentifiedAccessVerifier = try params.optionalBase64EncodedData(key: "unidentifiedAccess")
self.hasUnrestrictedUnidentifiedAccess = try params.optional(key: "unrestrictedUnidentifiedAccess") ?? false
private func getUnidentifiedAccess(forRecipientId recipientId: RecipientIdentifier) -> SSKUnidentifiedAccess? {
return self.udManager.getAccess(forRecipientId: recipientId)?.targetUnidentifiedAccess
}
}

@ -36,7 +36,7 @@ public class OWSMessageSend: NSObject {
public var hasUDAuthFailed = false
@objc
public let udAccessKey: SMKUDAccessKey?
public let unidentifiedAccess: SSKUnidentifiedAccess?
@objc
public let localNumber: String
@ -44,9 +44,6 @@ public class OWSMessageSend: NSObject {
@objc
public let isLocalNumber: Bool
@objc
public let senderCertificate: SMKSenderCertificate?
@objc
public let success: () -> Void
@ -65,21 +62,16 @@ public class OWSMessageSend: NSObject {
self.message = message
self.thread = thread
self.recipient = recipient
self.senderCertificate = senderCertificate
self.localNumber = localNumber
var udAccessKey: SMKUDAccessKey?
var isLocalNumber: Bool = false
if let recipientId = recipient.uniqueId {
udAccessKey = (udManager.supportsUnidentifiedDelivery(recipientId: recipientId)
? udManager.udAccessKeyForRecipient(recipientId)
: nil)
isLocalNumber = localNumber == recipientId
self.unidentifiedAccess = udManager.getAccess(forRecipientId: recipientId)?.targetUnidentifiedAccess
self.isLocalNumber = localNumber == recipientId
} else {
owsFailDebug("SignalRecipient missing recipientId")
self.isLocalNumber = false
self.unidentifiedAccess = nil
}
self.udAccessKey = udAccessKey
self.localNumber = localNumber
self.isLocalNumber = isLocalNumber
self.success = success
self.failure = failure
@ -87,6 +79,6 @@ public class OWSMessageSend: NSObject {
@objc
public var isUDSend: Bool {
return (!hasUDAuthFailed && udAccessKey != nil && senderCertificate != nil)
return (!hasUDAuthFailed && self.unidentifiedAccess != nil)
}
}

@ -998,12 +998,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
TSRequest *request = [OWSRequestFactory submitMessageRequestWithRecipient:recipient.uniqueId
messages:deviceMessages
timeStamp:message.timestamp];
if (messageSend.isUDSend) {
DDLogVerbose(@"UD send.");
[request useUDAuth:messageSend.udAccessKey];
}
timeStamp:message.timestamp
unidentifiedAccess:messageSend.unidentifiedAccess];
OWSWebSocketType webSocketType = (messageSend.isUDSend ? OWSWebSocketTypeUD : OWSWebSocketTypeDefault);
BOOL canMakeWebsocketRequests = ([TSSocketManager.shared canMakeRequestsOfType:webSocketType] &&
@ -1041,7 +1037,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
//
// 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];
[self.udManager setUnidentifiedAccessMode:UnidentifiedAccessModeDisabled
recipientId:recipient.uniqueId];
messageSend.hasUDAuthFailed = YES;
dispatch_async([OWSDispatch sendingQueue], ^{
[self sendMessageToRecipient:messageSend];
@ -1089,7 +1086,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
[SignalRecipient markRecipientAsRegisteredAndGet:recipient.recipientId transaction:transaction];
}];
[self handleMessageSentLocally:messageSend.message senderCertificate:messageSend.senderCertificate];
[self handleMessageSentLocally:messageSend.message
senderCertificate:messageSend.unidentifiedAccess.senderCertificate];
messageSend.success();
});
}
@ -1398,13 +1396,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
// To avoid deadlock, we need to ensure that our success/failure completions
// are called _off_ the main thread. Otherwise we'll deadlock if the main
// thread is blocked on opening a transaction.
TSRequest *request =
[OWSRequestFactory recipientPrekeyRequestWithRecipient:recipientId deviceId:[deviceId stringValue]];
if (messageSend.isUDSend) {
DDLogVerbose(@"UD prekey request.");
[request useUDAuth:messageSend.udAccessKey];
}
TSRequest *request = [OWSRequestFactory recipientPrekeyRequestWithRecipient:recipientId
deviceId:[deviceId stringValue]
unidentifiedAccess:messageSend.unidentifiedAccess];
[self.networkManager makeRequest:request
completionQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
@ -1508,7 +1502,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
serializedMessage = [secretCipher encryptMessageWithRecipientId:recipientId
deviceId:deviceId.intValue
paddedPlaintext:[plainText paddedMessageBody]
senderCertificate:messageSend.senderCertificate
senderCertificate:messageSend.unidentifiedAccess.senderCertificate
protocolContext:transaction
error:&error];
messageType = TSUnidentifiedSenderMessageType;

@ -12,38 +12,45 @@ public enum OWSUDError: Error {
case invalidData(description: String)
}
@objc
public enum UnidentifiedAccessMode: Int {
case unknown
case enabled
case disabled
case unrestricted
}
@objc public protocol OWSUDManager: class {
@objc func setup()
@objc func trustRoot() -> ECPublicKey
// MARK: - Recipient state
// MARK: - Recipient State
@objc func supportsUnidentifiedDelivery(recipientId: String) -> Bool
@objc
func setUnidentifiedAccessMode(_ mode: UnidentifiedAccessMode, recipientId: String)
@objc func setSupportsUnidentifiedDelivery(_ value: Bool, recipientId: String)
@objc
func getAccess(forRecipientId recipientId: RecipientIdentifier) -> SSKUnidentifiedAccessPair?
// 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?
@objc func udAccessKeyForRecipient(_ recipientId: RecipientIdentifier) -> SMKUDAccessKey?
// MARK: - Sender Certificate
// MARK: - Local State
// MARK: Sender Certificate
// We use completion handlers instead of a promise so that message sending
// logic can access the certificate data.
// logic can access the strongly typed certificate data.
@objc func ensureSenderCertificateObjC(success:@escaping (SMKSenderCertificate) -> Void,
failure:@escaping (Error) -> Void)
// MARK: - Unrestricted Access
// MARK: Unrestricted Access
@objc func shouldAllowUnrestrictedAccessLocal() -> Bool
@objc func setShouldAllowUnrestrictedAccessLocal(_ value: Bool)
@objc func shouldAllowUnrestrictedAccess(recipientId: String) -> Bool
@objc func setShouldAllowUnrestrictedAccess(recipientId: String, shouldAllowUnrestrictedAccess: Bool)
}
// MARK: -
@ -53,11 +60,13 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
private let dbConnection: YapDatabaseConnection
// MARK: Local Configuration State
private let kUDCollection = "kUDCollection"
private let kUDCurrentSenderCertificateKey = "kUDCurrentSenderCertificateKey"
private let kUDUnrestrictedAccessKey = "kUDUnrestrictedAccessKey"
private let kUDRecipientModeCollection = "kUDRecipientModeCollection"
private let kUDUnrestrictedAccessCollection = "kUDUnrestrictedAccessCollection"
// MARK: Recipient State
private let kUnidentifiedAccessCollection = "kUnidentifiedAccessCollection"
@objc
public required init(primaryStorage: OWSPrimaryStorage) {
@ -101,26 +110,61 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
// MARK: - Recipient state
@objc
public func supportsUnidentifiedDelivery(recipientId: String) -> Bool {
if tsAccountManager.localNumber() == recipientId {
return true
public func getAccess(forRecipientId recipientId: RecipientIdentifier) -> SSKUnidentifiedAccessPair? {
guard let theirAccessKey = self.udAccessKeyForRecipient(recipientId) else {
return nil
}
guard let ourSenderCertificate = self.senderCertificate() else {
return nil
}
return dbConnection.bool(forKey: recipientId, inCollection: kUDRecipientModeCollection, defaultValue: false)
guard let ourAccessKey: SMKUDAccessKey = {
if self.shouldAllowUnrestrictedAccessLocal() {
return SMKUDAccessKey(randomKeyData: ())
} else {
guard let localNumber = self.tsAccountManager.localNumber() else {
owsFailDebug("localNumber was unexpectedly nil")
return nil
}
return self.udAccessKeyForRecipient(localNumber)
}
}() else {
return nil
}
let targetUnidentifiedAccess = SSKUnidentifiedAccess(accessKey: theirAccessKey, senderCertificate: ourSenderCertificate)
let selfUnidentifiedAccess = SSKUnidentifiedAccess(accessKey: ourAccessKey, senderCertificate: ourSenderCertificate)
return SSKUnidentifiedAccessPair(targetUnidentifiedAccess: targetUnidentifiedAccess,
selfUnidentifiedAccess: selfUnidentifiedAccess)
}
@objc
public func setSupportsUnidentifiedDelivery(_ value: Bool, recipientId: String) {
if value {
dbConnection.setBool(true, forKey: recipientId, inCollection: kUDRecipientModeCollection)
} else {
dbConnection.removeObject(forKey: recipientId, inCollection: kUDRecipientModeCollection)
private func unidentifiedAccessMode(recipientId: RecipientIdentifier) -> UnidentifiedAccessMode {
if tsAccountManager.localNumber() == recipientId {
if shouldAllowUnrestrictedAccessLocal() {
return .unrestricted
} else {
return .enabled
}
}
guard let existingValue = dbConnection.object(forKey: recipientId, inCollection: kUnidentifiedAccessCollection) as? UnidentifiedAccessMode else {
return .unknown
}
return existingValue
}
@objc
public func setUnidentifiedAccessMode(_ mode: UnidentifiedAccessMode, recipientId: String) {
dbConnection.setObject(mode, forKey: recipientId, inCollection: kUnidentifiedAccessCollection)
}
// Returns the UD access key for a given recipient
// if we have a valid profile key for them.
@objc
public func udAccessKeyForRecipient(_ recipientId: String) -> SMKUDAccessKey? {
public func udAccessKeyForRecipient(_ recipientId: RecipientIdentifier) -> SMKUDAccessKey? {
guard let profileKey = profileManager.profileKeyData(forRecipientId: recipientId) else {
// Mark as "not a UD recipient".
return nil
@ -251,14 +295,4 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
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(recipientId: String, shouldAllowUnrestrictedAccess: Bool) {
dbConnection.setBool(shouldAllowUnrestrictedAccess, forKey: recipientId, inCollection: kUDUnrestrictedAccessCollection)
}
}

@ -7,6 +7,7 @@ NS_ASSUME_NONNULL_BEGIN
@class ECKeyPair;
@class OWSDevice;
@class PreKeyRecord;
@class SSKUnidentifiedAccess;
@class SignedPreKeyRecord;
@class TSRequest;
@ -35,7 +36,9 @@ typedef NS_ENUM(NSUInteger, TSVerificationTransport) { TSVerificationTransportVo
+ (TSRequest *)getMessagesRequest;
+ (TSRequest *)getProfileRequestWithRecipientId:(NSString *)recipientId;
+ (TSRequest *)getProfileRequestWithRecipientId:(NSString *)recipientId
unidentifiedAccess:(nullable SSKUnidentifiedAccess *)unidentifiedAccess
NS_SWIFT_NAME(getProfileRequest(recipientId:unidentifiedAccess:));
+ (TSRequest *)turnServerInfoRequest;
@ -58,7 +61,8 @@ typedef NS_ENUM(NSUInteger, TSVerificationTransport) { TSVerificationTransportVo
+ (TSRequest *)submitMessageRequestWithRecipient:(NSString *)recipientId
messages:(NSArray *)messages
timeStamp:(uint64_t)timeStamp;
timeStamp:(uint64_t)timeStamp
unidentifiedAccess:(nullable SSKUnidentifiedAccess *)unidentifiedAccess;
+ (TSRequest *)verifyCodeRequestWithVerificationCode:(NSString *)verificationCode
forNumber:(NSString *)phoneNumber
@ -72,7 +76,9 @@ typedef NS_ENUM(NSUInteger, TSVerificationTransport) { TSVerificationTransportVo
+ (TSRequest *)currentSignedPreKeyRequest;
+ (TSRequest *)recipientPrekeyRequestWithRecipient:(NSString *)recipientNumber deviceId:(NSString *)deviceId;
+ (TSRequest *)recipientPrekeyRequestWithRecipient:(NSString *)recipientNumber
deviceId:(NSString *)deviceId
unidentifiedAccess:(nullable SSKUnidentifiedAccess *)unidentifiedAccess;
+ (TSRequest *)registerSignedPrekeyRequestWithSignedPreKeyRecord:(SignedPreKeyRecord *)signedPreKey;

@ -124,11 +124,16 @@ NS_ASSUME_NONNULL_BEGIN
}
+ (TSRequest *)getProfileRequestWithRecipientId:(NSString *)recipientId
unidentifiedAccess:(nullable SSKUnidentifiedAccess *)unidentifiedAccess
{
OWSAssertDebug(recipientId.length > 0);
NSString *path = [NSString stringWithFormat:textSecureProfileAPIFormat, recipientId];
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}];
TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}];
if (unidentifiedAccess != nil) {
[self useUDAuthWithRequest:request accessKey:unidentifiedAccess.accessKey];
}
return request;
}
+ (TSRequest *)turnServerInfoRequest
@ -181,13 +186,20 @@ NS_ASSUME_NONNULL_BEGIN
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}];
}
+ (TSRequest *)recipientPrekeyRequestWithRecipient:(NSString *)recipientNumber deviceId:(NSString *)deviceId
+ (TSRequest *)recipientPrekeyRequestWithRecipient:(NSString *)recipientNumber
deviceId:(NSString *)deviceId
unidentifiedAccess:(nullable SSKUnidentifiedAccess *)unidentifiedAccess;
{
OWSAssertDebug(recipientNumber.length > 0);
OWSAssertDebug(deviceId.length > 0);
NSString *path = [NSString stringWithFormat:@"%@/%@/%@", textSecureKeysAPI, recipientNumber, deviceId];
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}];
TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}];
if (unidentifiedAccess != nil) {
[self useUDAuthWithRequest:request accessKey:unidentifiedAccess.accessKey];
}
return request;
}
+ (TSRequest *)registerForPushRequestWithPushIdentifier:(NSString *)identifier voipIdentifier:(NSString *)voipId
@ -316,6 +328,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (TSRequest *)submitMessageRequestWithRecipient:(NSString *)recipientId
messages:(NSArray *)messages
timeStamp:(uint64_t)timeStamp
unidentifiedAccess:(nullable SSKUnidentifiedAccess *)unidentifiedAccess
{
// NOTE: messages may be empty; See comments in OWSDeviceManager.
OWSAssertDebug(recipientId.length > 0);
@ -327,7 +340,11 @@ NS_ASSUME_NONNULL_BEGIN
@"timestamp" : @(timeStamp),
};
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"PUT" parameters:parameters];
TSRequest *request = [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"PUT" parameters:parameters];
if (unidentifiedAccess != nil) {
[self useUDAuthWithRequest:request accessKey:unidentifiedAccess.accessKey];
}
return request;
}
+ (TSRequest *)registerSignedPrekeyRequestWithSignedPreKeyRecord:(SignedPreKeyRecord *)signedPreKey
@ -462,6 +479,18 @@ NS_ASSUME_NONNULL_BEGIN
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}];
}
+ (void)useUDAuthWithRequest:(TSRequest *)request accessKey:(SMKUDAccessKey *)udAccessKey
{
OWSAssertDebug(request);
OWSAssertDebug(udAccessKey);
// Suppress normal auth headers.
request.shouldHaveAuthorizationHeaders = NO;
// Add UD auth header.
[request setValue:[udAccessKey.keyData base64EncodedString] forHTTPHeaderField:@"Unidentified-Access-Key"];
}
@end
NS_ASSUME_NONNULL_END

@ -28,8 +28,4 @@
method:(NSString *)method
parameters:(nullable NSDictionary<NSString *, id> *)parameters;
#pragma mark - UD
- (void)useUDAuth:(SMKUDAccessKey *)udAccessKey;
@end

@ -112,16 +112,4 @@
}
}
#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

@ -0,0 +1,47 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
@objc
public class SignalServiceProfile: NSObject {
public enum ValidationError: Error {
case invalid(description: String)
case invalidIdentityKey(description: String)
case invalidProfileName(description: String)
}
public let recipientId: String
public let identityKey: Data
public let profileNameEncrypted: Data?
public let avatarUrlPath: String?
public let unidentifiedAccessVerifier: Data?
public let hasUnrestrictedUnidentifiedAccess: Bool
public init(recipientId: String, responseObject: Any?) throws {
self.recipientId = recipientId
guard let params = ParamParser(responseObject: responseObject) else {
throw ValidationError.invalid(description: "invalid response: \(String(describing: responseObject))")
}
let identityKeyWithType = try params.requiredBase64EncodedData(key: "identityKey")
let kIdentityKeyLength = 33
guard identityKeyWithType.count == kIdentityKeyLength else {
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
self.profileNameEncrypted = try params.optionalBase64EncodedData(key: "name")
let avatarUrlPath: String? = try params.optional(key: "avatar")
self.avatarUrlPath = avatarUrlPath
self.unidentifiedAccessVerifier = try params.optionalBase64EncodedData(key: "unidentifiedAccess")
self.hasUnrestrictedUnidentifiedAccess = try params.optional(key: "unrestrictedUnidentifiedAccess") ?? false
}
}

@ -4,13 +4,17 @@
import Foundation
import PromiseKit
import SignalMetadataKit
protocol SignalServiceClient {
public typealias RecipientIdentifier = String
public protocol SignalServiceClient {
func getAvailablePreKeys() -> Promise<Int>
func registerPreKeys(identityKey: IdentityKey, signedPreKeyRecord: SignedPreKeyRecord, preKeyRecords: [PreKeyRecord]) -> Promise<Void>
func setCurrentSignedPreKey(_ signedPreKey: SignedPreKeyRecord) -> Promise<Void>
func requestUDSenderCertificate() -> Promise<Data>
func updateAcountAttributes() -> Promise<Void>
func retrieveProfile(recipientId: RecipientIdentifier, unidentifiedAccess: SSKUnidentifiedAccess?) -> Promise<SignalServiceProfile>
}
/// Based on libsignal-service-java's PushServiceSocket class
@ -25,7 +29,7 @@ public class SignalServiceRestClient: NSObject, SignalServiceClient {
return OWSErrorMakeUnableToProcessServerResponseError()
}
func getAvailablePreKeys() -> Promise<Int> {
public func getAvailablePreKeys() -> Promise<Int> {
Logger.debug("")
let request = OWSRequestFactory.availablePreKeysCountRequest()
@ -41,7 +45,7 @@ public class SignalServiceRestClient: NSObject, SignalServiceClient {
}
}
func registerPreKeys(identityKey: IdentityKey, signedPreKeyRecord: SignedPreKeyRecord, preKeyRecords: [PreKeyRecord]) -> Promise<Void> {
public func registerPreKeys(identityKey: IdentityKey, signedPreKeyRecord: SignedPreKeyRecord, preKeyRecords: [PreKeyRecord]) -> Promise<Void> {
Logger.debug("")
let request = OWSRequestFactory.registerPrekeysRequest(withPrekeyArray: preKeyRecords, identityKey: identityKey, signedPreKey: signedPreKeyRecord)
@ -83,4 +87,11 @@ public class SignalServiceRestClient: NSObject, SignalServiceClient {
})
return promise
}
public func retrieveProfile(recipientId: RecipientIdentifier, unidentifiedAccess: SSKUnidentifiedAccess?) -> Promise<SignalServiceProfile> {
let request = OWSRequestFactory.getProfileRequest(recipientId: recipientId, unidentifiedAccess: unidentifiedAccess)
return networkManager.makePromise(request: request).then { (task: URLSessionDataTask, responseObject: Any?) in
return try SignalServiceProfile(recipientId: recipientId, responseObject: responseObject)
}
}
}

@ -0,0 +1,31 @@
//
// 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