From 8f7ba1407f71d33e32bf6579986f96db1a4caf97 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Thu, 23 Jan 2020 10:04:36 +1100 Subject: [PATCH] Clean. Switched to generating ephemeral key-pair per proxy request. --- .../src/Loki/API/Clients/LokiHttpClient.swift | 6 +-- .../src/Loki/API/Clients/LokiSnodeProxy.swift | 41 ++++++++----------- .../src/Network/OWSSignalService.m | 8 +--- 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/SignalServiceKit/src/Loki/API/Clients/LokiHttpClient.swift b/SignalServiceKit/src/Loki/API/Clients/LokiHttpClient.swift index 368674095..aebebd310 100644 --- a/SignalServiceKit/src/Loki/API/Clients/LokiHttpClient.swift +++ b/SignalServiceKit/src/Loki/API/Clients/LokiHttpClient.swift @@ -2,7 +2,6 @@ import PromiseKit internal class LokiHttpClient { enum HttpError: LocalizedError { - /// Wraps TSNetworkManager failure callback params in a single throwable error case networkError(code: Int, response: Any?, underlyingError: Error?) public var errorDescription: String? { @@ -14,7 +13,7 @@ internal class LokiHttpClient { func perform(_ request: TSRequest, withCompletionQueue queue: DispatchQueue = DispatchQueue.main) -> Promise { return TSNetworkManager.shared().perform(request, withCompletionQueue: queue).map { $0.responseObject }.recover { error -> Promise in - throw LokiHttpClient.HttpError.from(error: error) ?? error + throw HttpError.from(error: error) ?? error } } } @@ -24,12 +23,13 @@ extension LokiHttpClient.HttpError { if let error = error as? NetworkManagerError { if case NetworkManagerError.taskError(_, let underlyingError) = error, let nsError = underlyingError as? NSError { var response = nsError.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] + // Deserialize response if needed if let data = response as? Data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? JSON { response = json } return LokiHttpClient.HttpError.networkError(code: error.statusCode, response: response, underlyingError: underlyingError) } - return LokiHttpClient.HttpError.networkError(code: error.statusCode, response: nil, underlyingError: nil) + return LokiHttpClient.HttpError.networkError(code: error.statusCode, response: nil, underlyingError: error) } return nil } diff --git a/SignalServiceKit/src/Loki/API/Clients/LokiSnodeProxy.swift b/SignalServiceKit/src/Loki/API/Clients/LokiSnodeProxy.swift index 7a0aa05fd..35536e85e 100644 --- a/SignalServiceKit/src/Loki/API/Clients/LokiSnodeProxy.swift +++ b/SignalServiceKit/src/Loki/API/Clients/LokiSnodeProxy.swift @@ -2,6 +2,7 @@ import PromiseKit internal class LokiSnodeProxy: LokiHttpClient { internal let target: LokiAPITarget + private let keyPair: ECKeyPair internal enum Error : LocalizedError { case invalidPublicKeys @@ -30,34 +31,20 @@ internal class LokiSnodeProxy: LokiHttpClient { return manager }() - // MARK: - Ephemeral key - private var _kp: ECKeyPair - private var _lastGenerated: TimeInterval - private let keyPairRefreshTime: TimeInterval = 3 * 60 * 1000 // 3 minutes // MARK: - Class functions init(target: LokiAPITarget) { self.target = target - _kp = Curve25519.generateKeyPair() - _lastGenerated = Date().timeIntervalSince1970 + keyPair = Curve25519.generateKeyPair() super.init() } - private func getKeyPair() -> ECKeyPair { - if (Date().timeIntervalSince1970 > _lastGenerated + keyPairRefreshTime) { - _kp = Curve25519.generateKeyPair() - _lastGenerated = Date().timeIntervalSince1970 - } - return _kp - } - override func perform(_ request: TSRequest, withCompletionQueue queue: DispatchQueue = DispatchQueue.main) -> Promise { guard let targetHexEncodedPublicKeys = target.publicKeys else { return Promise(error: Error.invalidPublicKeys) } - let keyPair = getKeyPair() guard let symmetricKey = try? Curve25519.generateSharedSecret(fromPublicKey: Data(hex: targetHexEncodedPublicKeys.encryption), privateKey: keyPair.privateKey) else { return Promise(error: Error.failedToEncryptRequest) } @@ -65,13 +52,18 @@ internal class LokiSnodeProxy: LokiHttpClient { return LokiAPI.getRandomSnode().then { snode -> Promise in let url = "\(snode.address):\(snode.port)/proxy" print("[Loki][Snode proxy] Proxy request to \(self.target) via \(snode).") - var peepee = request.parameters - let jsonBodyData = try JSONSerialization.data(withJSONObject: peepee, options: []) - let jsonBodyString = String(bytes: jsonBodyData, encoding: .utf8) - let params: [String : Any] = [ "method" : request.httpMethod, "body" : jsonBodyString, "headers" : self.getHeaders(request: request) ] - let jsonParams = try JSONSerialization.data(withJSONObject: params, options: []) - let ivAndCipherText = try DiffieHellman.encrypt(jsonParams, using: symmetricKey) - let headers = [ "X-Sender-Public-Key" : keyPair.publicKey.hexadecimalString, "X-Target-Snode-Key" : targetHexEncodedPublicKeys.identification] + let requestParams = try JSONSerialization.data(withJSONObject: request.parameters, options: []) + let params: [String : Any] = [ + "method" : request.httpMethod, + "body" : String(bytes: requestParams, encoding: .utf8), + "headers" : self.getHeaders(request: request) + ] + let proxyParams = try JSONSerialization.data(withJSONObject: params, options: []) + let ivAndCipherText = try DiffieHellman.encrypt(proxyParams, using: symmetricKey) + let headers = [ + "X-Sender-Public-Key" : self.keyPair.publicKey.hexadecimalString, + "X-Target-Snode-Key" : targetHexEncodedPublicKeys.identification + ] return self.post(url: url, body: ivAndCipherText, headers: headers, timeoutInterval: request.timeoutInterval) }.map { response in guard response is Data, let cipherText = Data(base64Encoded: response as! Data) else { @@ -89,10 +81,9 @@ internal class LokiSnodeProxy: LokiHttpClient { let success = (200..<300).contains(code) var body: Any? = nil if let string = json["body"] as? String { + body = string if let jsonBody = try? JSONSerialization.jsonObject(with: string.data(using: .utf8)!, options: .allowFragments) as? [String: Any] { body = jsonBody - } else { - body = string } } @@ -107,6 +98,8 @@ internal class LokiSnodeProxy: LokiHttpClient { } } + // MARK:- Private functions + private func getHeaders(request: TSRequest) -> [String: Any] { guard let headers = request.allHTTPHeaderFields else { return [:] diff --git a/SignalServiceKit/src/Network/OWSSignalService.m b/SignalServiceKit/src/Network/OWSSignalService.m index 71fc0297f..208c95f24 100644 --- a/SignalServiceKit/src/Network/OWSSignalService.m +++ b/SignalServiceKit/src/Network/OWSSignalService.m @@ -193,13 +193,7 @@ NSString *const kNSNotificationName_IsCensorshipCircumventionActiveDidChange = sessionManager.securityPolicy = securityPolicy; sessionManager.requestSerializer = [AFJSONRequestSerializer serializer]; sessionManager.requestSerializer.HTTPShouldHandleCookies = NO; - - // We could get JSON or text responses - NSArray *serializers = @[ - [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments], - [AFHTTPResponseSerializer serializer] - ]; - sessionManager.responseSerializer = [AFCompoundResponseSerializer compoundSerializerWithResponseSerializers:serializers]; + sessionManager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments]; return sessionManager; }