From f0d56cdab055ec4e14cdadae2cc8d81cb526d35e Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Thu, 18 Jun 2020 11:16:34 +1000 Subject: [PATCH] Fix clock out of sync error not propagating --- .../src/Loki/API/LokiFileServerProxy.swift | 8 ++++---- .../src/Loki/API/LokiHTTPClient.swift | 2 +- .../OnionRequestAPI+Encryption.swift | 6 +++--- .../API/Onion Requests/OnionRequestAPI.swift | 17 +++++++++++------ .../src/Loki/API/Utilities/HTTP.swift | 4 ++-- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift b/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift index d9221c181..396259616 100644 --- a/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift +++ b/SignalServiceKit/src/Loki/API/LokiFileServerProxy.swift @@ -58,7 +58,7 @@ internal class LokiFileServerProxy : LokiHTTPClient { let parametersAsString: String if let tsRequest = request as? TSRequest { headers["Content-Type"] = "application/json" - let parametersAsData = try JSONSerialization.data(withJSONObject: tsRequest.parameters, options: []) + let parametersAsData = try JSONSerialization.data(withJSONObject: tsRequest.parameters, options: [ .fragmentsAllowed ]) parametersAsString = !tsRequest.parameters.isEmpty ? String(bytes: parametersAsData, encoding: .utf8)! : "null" } else { headers["Content-Type"] = request.allHTTPHeaderFields!["Content-Type"] @@ -74,7 +74,7 @@ internal class LokiFileServerProxy : LokiHTTPClient { "method" : request.httpMethod, "headers" : headers ] - let proxyRequestParametersAsData = try JSONSerialization.data(withJSONObject: proxyRequestParameters, options: []) + let proxyRequestParametersAsData = try JSONSerialization.data(withJSONObject: proxyRequestParameters, options: [ .fragmentsAllowed ]) let ivAndCipherText = try DiffieHellman.encrypt(proxyRequestParametersAsData, using: symmetricKey) let base64EncodedPublicKey = Data(hex: keyPair.hexEncodedPublicKey).base64EncodedString() // The file server expects an 05 prefixed public key let proxyRequestHeaders = [ @@ -103,7 +103,7 @@ internal class LokiFileServerProxy : LokiHTTPClient { task.resume() return promise }.map2 { rawResponse in - guard let responseAsData = rawResponse as? Data, let responseAsJSON = try? JSONSerialization.jsonObject(with: responseAsData, options: .allowFragments) as? JSON, let base64EncodedCipherText = responseAsJSON["data"] as? String, + guard let responseAsData = rawResponse as? Data, let responseAsJSON = try? JSONSerialization.jsonObject(with: responseAsData, options: [ .fragmentsAllowed ]) as? JSON, let base64EncodedCipherText = responseAsJSON["data"] as? String, let meta = responseAsJSON["meta"] as? JSON, let statusCode = meta["code"] as? Int, let cipherText = Data(base64Encoded: base64EncodedCipherText) else { print("[Loki] Received an invalid response.") throw Error.proxyResponseParsingFailed @@ -112,7 +112,7 @@ internal class LokiFileServerProxy : LokiHTTPClient { guard isSuccess else { throw HTTPError.networkError(code: statusCode, response: nil, underlyingError: Error.fileServerHTTPError(code: statusCode, message: nil)) } let uncheckedJSONAsData = try DiffieHellman.decrypt(cipherText, using: symmetricKey) if uncheckedJSONAsData.isEmpty { return () } - let uncheckedJSON = try? JSONSerialization.jsonObject(with: uncheckedJSONAsData, options: .allowFragments) as? JSON + let uncheckedJSON = try? JSONSerialization.jsonObject(with: uncheckedJSONAsData, options: [ .fragmentsAllowed ]) as? JSON guard let json = uncheckedJSON else { throw HTTPError.networkError(code: -1, response: nil, underlyingError: Error.proxyResponseParsingFailed) } return json }.done2 { rawResponse in diff --git a/SignalServiceKit/src/Loki/API/LokiHTTPClient.swift b/SignalServiceKit/src/Loki/API/LokiHTTPClient.swift index 8f3989eff..99763ffcf 100644 --- a/SignalServiceKit/src/Loki/API/LokiHTTPClient.swift +++ b/SignalServiceKit/src/Loki/API/LokiHTTPClient.swift @@ -44,7 +44,7 @@ public extension LokiHTTPClient { 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 { + if let data = response as? Data, let json = try? JSONSerialization.jsonObject(with: data, options: [ .fragmentsAllowed ]) as? JSON { response = json } return LokiHTTPClient.HTTPError.networkError(code: error.statusCode, response: response, underlyingError: underlyingError) diff --git a/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI+Encryption.swift b/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI+Encryption.swift index ab49ec332..5a11a954a 100644 --- a/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI+Encryption.swift +++ b/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI+Encryption.swift @@ -36,11 +36,11 @@ extension OnionRequestAPI { DispatchQueue.global(qos: .userInitiated).async { do { guard JSONSerialization.isValidJSONObject(payload) else { return seal.reject(HTTP.Error.invalidJSON) } - let payloadAsData = try JSONSerialization.data(withJSONObject: payload, options: []) + let payloadAsData = try JSONSerialization.data(withJSONObject: payload, options: [ .fragmentsAllowed ]) let payloadAsString = String(data: payloadAsData, encoding: .utf8)! // Snodes only accept this as a string let wrapper: JSON = [ "body" : payloadAsString, "headers" : "" ] guard JSONSerialization.isValidJSONObject(wrapper) else { return seal.reject(HTTP.Error.invalidJSON) } - let plaintext = try JSONSerialization.data(withJSONObject: wrapper, options: []) + let plaintext = try JSONSerialization.data(withJSONObject: wrapper, options: [ .fragmentsAllowed ]) let result = try encrypt(plaintext, forSnode: snode) seal.fulfill(result) } catch (let error) { @@ -61,7 +61,7 @@ extension OnionRequestAPI { ] do { guard JSONSerialization.isValidJSONObject(parameters) else { return seal.reject(HTTP.Error.invalidJSON) } - let plaintext = try JSONSerialization.data(withJSONObject: parameters, options: []) + let plaintext = try JSONSerialization.data(withJSONObject: parameters, options: [ .fragmentsAllowed ]) let result = try encrypt(plaintext, forSnode: lhs) seal.fulfill(result) } catch (let error) { diff --git a/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI.swift b/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI.swift index 2b0a0c2c2..a4b042929 100644 --- a/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI.swift +++ b/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI.swift @@ -228,12 +228,17 @@ public enum OnionRequestAPI { let gcm = GCM(iv: iv.bytes, tagLength: Int(gcmTagSize), mode: .combined) let aes = try AES(key: targetSnodeSymmetricKey.bytes, blockMode: gcm, padding: .noPadding) let data = Data(try aes.decrypt(ciphertext.bytes)) - guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? JSON, - let bodyAsString = json["body"] as? String, let bodyAsData = bodyAsString.data(using: .utf8), - let body = try JSONSerialization.jsonObject(with: bodyAsData, options: []) as? JSON, - let statusCode = json["status"] as? Int else { return seal.reject(HTTP.Error.invalidJSON) } - guard 200...299 ~= statusCode else { return seal.reject(Error.httpRequestFailedAtTargetSnode(statusCode: UInt(statusCode), json: body)) } - seal.fulfill(body) + guard let json = try JSONSerialization.jsonObject(with: data, options: [ .fragmentsAllowed ]) as? JSON, + let bodyAsString = json["body"] as? String, let statusCode = json["status"] as? Int else { return seal.reject(HTTP.Error.invalidJSON) } + if statusCode == 406 { // Clock out of sync + print("[Loki] The user's clock is out of sync with the service node network.") + seal.reject(LokiAPI.LokiAPIError.clockOutOfSync) + } else { + guard let bodyAsData = bodyAsString.data(using: .utf8), + let body = try JSONSerialization.jsonObject(with: bodyAsData, options: [ .fragmentsAllowed ]) as? JSON else { return seal.reject(HTTP.Error.invalidJSON) } + guard 200...299 ~= statusCode else { return seal.reject(Error.httpRequestFailedAtTargetSnode(statusCode: UInt(statusCode), json: body)) } + seal.fulfill(body) + } } catch (let error) { seal.reject(error) } diff --git a/SignalServiceKit/src/Loki/API/Utilities/HTTP.swift b/SignalServiceKit/src/Loki/API/Utilities/HTTP.swift index 68940dd78..f866dfe11 100644 --- a/SignalServiceKit/src/Loki/API/Utilities/HTTP.swift +++ b/SignalServiceKit/src/Loki/API/Utilities/HTTP.swift @@ -46,7 +46,7 @@ internal enum HTTP { if let parameters = parameters { do { guard JSONSerialization.isValidJSONObject(parameters) else { return Promise(error: Error.invalidJSON) } - request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: []) + request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: [ .fragmentsAllowed ]) } catch (let error) { return Promise(error: error) } @@ -70,7 +70,7 @@ internal enum HTTP { } let statusCode = UInt(response.statusCode) var json: JSON? = nil - if let j = try? JSONSerialization.jsonObject(with: data, options: []) as? JSON { + if let j = try? JSONSerialization.jsonObject(with: data, options: [ .fragmentsAllowed ]) as? JSON { json = j } else if let result = String(data: data, encoding: .utf8) { json = [ "result" : result ]