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
pull/732/head
Morgan Pretty 3 years ago
parent c934415355
commit f4d4e7d810

@ -227,6 +227,7 @@ public enum OpenGroupAPI {
public static func capabilities( public static func capabilities(
_ db: Database, _ db: Database,
server: String, server: String,
forceBlinded: Bool = false,
using dependencies: SMKDependencies = SMKDependencies() using dependencies: SMKDependencies = SMKDependencies()
) -> Promise<(OnionRequestResponseInfoType, Capabilities)> { ) -> Promise<(OnionRequestResponseInfoType, Capabilities)> {
return OpenGroupAPI return OpenGroupAPI
@ -236,6 +237,7 @@ public enum OpenGroupAPI {
server: server, server: server,
endpoint: .capabilities endpoint: .capabilities
), ),
forceBlinded: forceBlinded,
using: dependencies using: dependencies
) )
.decoded(as: Capabilities.self, on: OpenGroupAPI.workQueue, using: dependencies) .decoded(as: Capabilities.self, on: OpenGroupAPI.workQueue, using: dependencies)
@ -1260,6 +1262,7 @@ public enum OpenGroupAPI {
messageBytes: Bytes, messageBytes: Bytes,
for serverName: String, for serverName: String,
fallbackSigningType signingType: SessionId.Prefix, fallbackSigningType signingType: SessionId.Prefix,
forceBlinded: Bool = false,
using dependencies: SMKDependencies = SMKDependencies() using dependencies: SMKDependencies = SMKDependencies()
) -> (publicKey: String, signature: Bytes)? { ) -> (publicKey: String, signature: Bytes)? {
guard guard
@ -1279,7 +1282,7 @@ public enum OpenGroupAPI {
.defaulting(to: []) .defaulting(to: [])
// If we have no capabilities or if the server supports blinded keys then sign using the blinded key // 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 { guard let blindedKeyPair: Box.KeyPair = dependencies.sodium.blindedKeyPair(serverPublicKey: serverPublicKey, edKeyPair: userEdKeyPair, genericHash: dependencies.genericHash) else {
return nil return nil
} }
@ -1326,6 +1329,7 @@ public enum OpenGroupAPI {
request: URLRequest, request: URLRequest,
for serverName: String, for serverName: String,
with serverPublicKey: String, with serverPublicKey: String,
forceBlinded: Bool = false,
using dependencies: SMKDependencies = SMKDependencies() using dependencies: SMKDependencies = SMKDependencies()
) -> URLRequest? { ) -> URLRequest? {
guard let url: URL = request.url else { return nil } guard let url: URL = request.url else { return nil }
@ -1366,7 +1370,7 @@ public enum OpenGroupAPI {
.appending(contentsOf: bodyHash ?? []) .appending(contentsOf: bodyHash ?? [])
/// Sign the above message /// 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 return nil
} }
@ -1386,6 +1390,7 @@ public enum OpenGroupAPI {
private static func send<T: Encodable>( private static func send<T: Encodable>(
_ db: Database, _ db: Database,
request: Request<T, Endpoint>, request: Request<T, Endpoint>,
forceBlinded: Bool = false,
using dependencies: SMKDependencies = SMKDependencies() using dependencies: SMKDependencies = SMKDependencies()
) -> Promise<(OnionRequestResponseInfoType, Data?)> { ) -> Promise<(OnionRequestResponseInfoType, Data?)> {
let urlRequest: URLRequest let urlRequest: URLRequest
@ -1406,7 +1411,7 @@ public enum OpenGroupAPI {
guard let publicKey: String = maybePublicKey else { return Promise(error: OpenGroupAPIError.noPublicKey) } guard let publicKey: String = maybePublicKey else { return Promise(error: OpenGroupAPIError.noPublicKey) }
// Attempt to sign the request with the new auth // 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) return Promise(error: OpenGroupAPIError.signingFailed)
} }

@ -16,6 +16,9 @@ public enum OnionRequestAPIError: LocalizedError {
switch self { switch self {
case .httpRequestFailedAtDestination(let statusCode, let data, let destination): case .httpRequestFailedAtDestination(let statusCode, let data, let destination):
if statusCode == 429 { return "Rate limited." } 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) { if let errorResponse: String = String(data: data, encoding: .utf8) {
return "HTTP request failed at destination (\(destination)) with status code: \(statusCode), error body: \(errorResponse)." return "HTTP request failed at destination (\(destination)) with status code: \(statusCode), error body: \(errorResponse)."
} }

@ -700,51 +700,70 @@ public enum OnionRequestAPI: OnionRequestAPIType {
do { do {
let data: Data = try AESGCM.decrypt(responseData, with: destinationSymmetricKey) 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 // Process the bencoded response
// parts to properly process it guard let processedResponse: (info: ResponseInfo, body: Data?) = process(bencodedData: data) else {
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) return seal.reject(HTTP.Error.invalidResponse)
} }
let infoStringStartIndex: String.Index = responseString.index(responseString.startIndex, offsetBy: "l\(infoLength):".count) // Custom handle a clock out of sync error (v4 returns '425' but included the '406'
let infoStringEndIndex: String.Index = responseString.index(infoStringStartIndex, offsetBy: infoLength) // just in case)
let infoString: String = String(responseString[infoStringStartIndex..<infoStringEndIndex]) guard processedResponse.info.code != 406 && processedResponse.info.code != 425 else {
guard let infoStringData: Data = infoString.data(using: .utf8), let responseInfo: ResponseInfo = try? JSONDecoder().decode(ResponseInfo.self, from: infoStringData) else {
return seal.reject(HTTP.Error.invalidResponse)
}
// Custom handle a clock out of sync error (v4 returns '425' but included the '406' just in case)
guard responseInfo.code != 406 && responseInfo.code != 425 else {
SNLog("The user's clock is out of sync with the service node network.") SNLog("The user's clock is out of sync with the service node network.")
return seal.reject(SnodeAPIError.clockOutOfSync) return seal.reject(SnodeAPIError.clockOutOfSync)
} }
guard responseInfo.code != 401 else { // Signature verification failed guard processedResponse.info.code != 401 else { // Signature verification failed
SNLog("Failed to verify the signature.") SNLog("Failed to verify the signature.")
return seal.reject(SnodeAPIError.signatureVerificationFailed) return seal.reject(SnodeAPIError.signatureVerificationFailed)
} }
// Handle error status codes // Handle error status codes
guard 200...299 ~= responseInfo.code else { guard 200...299 ~= processedResponse.info.code else {
return seal.reject( return seal.reject(
OnionRequestAPIError.httpRequestFailedAtDestination( OnionRequestAPIError.httpRequestFailedAtDestination(
statusCode: UInt(responseInfo.code), statusCode: UInt(processedResponse.info.code),
data: data, data: data,
destination: destination destination: destination
) )
) )
} }
return seal.fulfill(processedResponse)
}
catch {
return seal.reject(error)
}
}
}
public static func process(bencodedData data: Data) -> (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..<infoStringEndIndex])
guard let infoStringData: Data = infoString.data(using: .utf8), let responseInfo: ResponseInfo = try? JSONDecoder().decode(ResponseInfo.self, from: infoStringData) else {
return nil
}
// Custom handle a clock out of sync error (v4 returns '425' but included the '406' just in case)
guard responseInfo.code != 406 && responseInfo.code != 425 else { return nil }
guard responseInfo.code != 401 else { return nil }
// If there is no data in the response then just return the ResponseInfo // If there is no data in the response then just return the ResponseInfo
guard responseString.count > "l\(infoLength)\(infoString)e".count else { guard responseString.count > "l\(infoLength)\(infoString)e".count else {
return seal.fulfill((responseInfo, nil)) return (responseInfo, nil)
} }
// Extract the response data as well // Extract the response data as well
@ -752,7 +771,7 @@ public enum OnionRequestAPI: OnionRequestAPIType {
let dataStringParts: [String.SubSequence] = dataString.split(separator: ":") 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 { guard dataStringParts.count > 1, let finalDataLength: Int = Int(dataStringParts[0]), let suffixData: Data = "e".data(using: .utf8) else {
return seal.reject(HTTP.Error.invalidResponse) return nil
} }
let dataBytes: Array<UInt8> = Array(data) let dataBytes: Array<UInt8> = Array(data)
@ -761,11 +780,6 @@ public enum OnionRequestAPI: OnionRequestAPIType {
let finalDataBytes: ArraySlice<UInt8> = dataBytes[dataStartIndex..<dataEndIndex] let finalDataBytes: ArraySlice<UInt8> = dataBytes[dataStartIndex..<dataEndIndex]
let finalData: Data = Data(finalDataBytes) let finalData: Data = Data(finalDataBytes)
return seal.fulfill((responseInfo, finalData)) return (responseInfo, finalData)
}
catch {
return seal.reject(error)
}
}
} }
} }

Loading…
Cancel
Save