From f4d4e7d81092e363f4232a1cd70096ae42205ebf Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Mon, 7 Nov 2022 10:18:59 +1100 Subject: [PATCH] Fixed an issue with blinding being switched on Fixed an issue where a user that was part of an open group before blinding was enabled wouldn't be able to access the group once blinding was switched on Updated the code to better support errors sent from SOGS as bencoded data --- .../Open Groups/OpenGroupAPI.swift | 11 ++- .../Models/OnionRequestAPIError.swift | 3 + SessionSnodeKit/OnionRequestAPI.swift | 98 +++++++++++-------- 3 files changed, 67 insertions(+), 45 deletions(-) diff --git a/SessionMessagingKit/Open Groups/OpenGroupAPI.swift b/SessionMessagingKit/Open Groups/OpenGroupAPI.swift index 4d32c974b..051ba7cc2 100644 --- a/SessionMessagingKit/Open Groups/OpenGroupAPI.swift +++ b/SessionMessagingKit/Open Groups/OpenGroupAPI.swift @@ -227,6 +227,7 @@ public enum OpenGroupAPI { public static func capabilities( _ db: Database, server: String, + forceBlinded: Bool = false, using dependencies: SMKDependencies = SMKDependencies() ) -> Promise<(OnionRequestResponseInfoType, Capabilities)> { return OpenGroupAPI @@ -236,6 +237,7 @@ public enum OpenGroupAPI { server: server, endpoint: .capabilities ), + forceBlinded: forceBlinded, using: dependencies ) .decoded(as: Capabilities.self, on: OpenGroupAPI.workQueue, using: dependencies) @@ -1260,6 +1262,7 @@ public enum OpenGroupAPI { messageBytes: Bytes, for serverName: String, fallbackSigningType signingType: SessionId.Prefix, + forceBlinded: Bool = false, using dependencies: SMKDependencies = SMKDependencies() ) -> (publicKey: String, signature: Bytes)? { guard @@ -1279,7 +1282,7 @@ public enum OpenGroupAPI { .defaulting(to: []) // If we have no capabilities or if the server supports blinded keys then sign using the blinded key - if capabilities.isEmpty || capabilities.contains(.blind) { + if forceBlinded || capabilities.isEmpty || capabilities.contains(.blind) { guard let blindedKeyPair: Box.KeyPair = dependencies.sodium.blindedKeyPair(serverPublicKey: serverPublicKey, edKeyPair: userEdKeyPair, genericHash: dependencies.genericHash) else { return nil } @@ -1326,6 +1329,7 @@ public enum OpenGroupAPI { request: URLRequest, for serverName: String, with serverPublicKey: String, + forceBlinded: Bool = false, using dependencies: SMKDependencies = SMKDependencies() ) -> URLRequest? { guard let url: URL = request.url else { return nil } @@ -1366,7 +1370,7 @@ public enum OpenGroupAPI { .appending(contentsOf: bodyHash ?? []) /// Sign the above message - guard let signResult: (publicKey: String, signature: Bytes) = sign(db, messageBytes: messageBytes, for: serverName, fallbackSigningType: .unblinded, using: dependencies) else { + guard let signResult: (publicKey: String, signature: Bytes) = sign(db, messageBytes: messageBytes, for: serverName, fallbackSigningType: .unblinded, forceBlinded: forceBlinded, using: dependencies) else { return nil } @@ -1386,6 +1390,7 @@ public enum OpenGroupAPI { private static func send( _ db: Database, request: Request, + forceBlinded: Bool = false, using dependencies: SMKDependencies = SMKDependencies() ) -> Promise<(OnionRequestResponseInfoType, Data?)> { let urlRequest: URLRequest @@ -1406,7 +1411,7 @@ public enum OpenGroupAPI { guard let publicKey: String = maybePublicKey else { return Promise(error: OpenGroupAPIError.noPublicKey) } // Attempt to sign the request with the new auth - guard let signedRequest: URLRequest = sign(db, request: urlRequest, for: request.server, with: publicKey, using: dependencies) else { + guard let signedRequest: URLRequest = sign(db, request: urlRequest, for: request.server, with: publicKey, forceBlinded: forceBlinded, using: dependencies) else { return Promise(error: OpenGroupAPIError.signingFailed) } diff --git a/SessionSnodeKit/Models/OnionRequestAPIError.swift b/SessionSnodeKit/Models/OnionRequestAPIError.swift index 3b75fe124..43e70da73 100644 --- a/SessionSnodeKit/Models/OnionRequestAPIError.swift +++ b/SessionSnodeKit/Models/OnionRequestAPIError.swift @@ -16,6 +16,9 @@ public enum OnionRequestAPIError: LocalizedError { switch self { case .httpRequestFailedAtDestination(let statusCode, let data, let destination): if statusCode == 429 { return "Rate limited." } + if let processedResponseBodyData: Data = OnionRequestAPI.process(bencodedData: data)?.body, let errorResponse: String = String(data: processedResponseBodyData, encoding: .utf8) { + return "HTTP request failed at destination (\(destination)) with status code: \(statusCode), error body: \(errorResponse)." + } if let errorResponse: String = String(data: data, encoding: .utf8) { return "HTTP request failed at destination (\(destination)) with status code: \(statusCode), error body: \(errorResponse)." } diff --git a/SessionSnodeKit/OnionRequestAPI.swift b/SessionSnodeKit/OnionRequestAPI.swift index 5a7e2fd45..d2a71b66c 100644 --- a/SessionSnodeKit/OnionRequestAPI.swift +++ b/SessionSnodeKit/OnionRequestAPI.swift @@ -700,72 +700,86 @@ public enum OnionRequestAPI: OnionRequestAPIType { do { let data: Data = try AESGCM.decrypt(responseData, with: destinationSymmetricKey) - // The data will be in the form of `l123:jsone` or `l123:json456:bodye` so we need to break the data into - // parts to properly process it - guard let responseString: String = String(data: data, encoding: .ascii), responseString.starts(with: "l") else { - return seal.reject(HTTP.Error.invalidResponse) - } - - let stringParts: [String.SubSequence] = responseString.split(separator: ":") - - guard stringParts.count > 1, let infoLength: Int = Int(stringParts[0].suffix(from: stringParts[0].index(stringParts[0].startIndex, offsetBy: 1))) else { - return seal.reject(HTTP.Error.invalidResponse) - } - - let infoStringStartIndex: String.Index = responseString.index(responseString.startIndex, offsetBy: "l\(infoLength):".count) - let infoStringEndIndex: String.Index = responseString.index(infoStringStartIndex, offsetBy: infoLength) - let infoString: String = String(responseString[infoStringStartIndex.. "l\(infoLength)\(infoString)e".count else { - return seal.fulfill((responseInfo, nil)) - } - - // Extract the response data as well - let dataString: String = String(responseString.suffix(from: infoStringEndIndex)) - let dataStringParts: [String.SubSequence] = dataString.split(separator: ":") - - guard dataStringParts.count > 1, let finalDataLength: Int = Int(dataStringParts[0]), let suffixData: Data = "e".data(using: .utf8) else { - return seal.reject(HTTP.Error.invalidResponse) - } - - let dataBytes: Array = Array(data) - let dataEndIndex: Int = (dataBytes.count - suffixData.count) - let dataStartIndex: Int = (dataEndIndex - finalDataLength) - let finalDataBytes: ArraySlice = dataBytes[dataStartIndex.. (info: ResponseInfo, body: Data?)? { + // The data will be in the form of `l123:jsone` or `l123:json456:bodye` so we need to break the data + // into parts to properly process it + guard let responseString: String = String(data: data, encoding: .ascii), responseString.starts(with: "l") else { + return nil + } + + let stringParts: [String.SubSequence] = responseString.split(separator: ":") + + guard stringParts.count > 1, let infoLength: Int = Int(stringParts[0].suffix(from: stringParts[0].index(stringParts[0].startIndex, offsetBy: 1))) else { + return nil + } + + let infoStringStartIndex: String.Index = responseString.index(responseString.startIndex, offsetBy: "l\(infoLength):".count) + let infoStringEndIndex: String.Index = responseString.index(infoStringStartIndex, offsetBy: infoLength) + let infoString: String = String(responseString[infoStringStartIndex.. "l\(infoLength)\(infoString)e".count else { + return (responseInfo, nil) + } + + // Extract the response data as well + let dataString: String = String(responseString.suffix(from: infoStringEndIndex)) + let dataStringParts: [String.SubSequence] = dataString.split(separator: ":") + + guard dataStringParts.count > 1, let finalDataLength: Int = Int(dataStringParts[0]), let suffixData: Data = "e".data(using: .utf8) else { + return nil + } + + let dataBytes: Array = Array(data) + let dataEndIndex: Int = (dataBytes.count - suffixData.count) + let dataStartIndex: Int = (dataEndIndex - finalDataLength) + let finalDataBytes: ArraySlice = dataBytes[dataStartIndex..