pull/148/head
gmbnt 4 years ago
parent 6b65a05d23
commit fd037c2a88

@ -1,4 +1,5 @@
import PromiseKit
import SignalMetadataKit
// TODO: Test path snodes as well
@ -29,11 +30,19 @@ internal enum OnionRequestAPI {
// MARK: Error
internal enum Error : LocalizedError {
case insufficientSnodes
case snodePublicKeySetMissing
case symmetricKeyGenerationFailed
case jsonSerializationFailed
case encryptionFailed
case generic
var errorDescription: String? {
switch self {
case .insufficientSnodes: return "Couldn't find enough snodes to build a path."
case .snodePublicKeySetMissing: return "Missing snode public key set."
case .symmetricKeyGenerationFailed: return "Couldn't generate symmetric key."
case .jsonSerializationFailed: return "Couldn't serialize JSON."
case .encryptionFailed: return "Couldn't encrypt request."
case .generic: return "An error occurred."
}
}
@ -109,6 +118,45 @@ internal enum OnionRequestAPI {
}
}
private static func getCanonicalHeaders(for request: TSRequest) -> [String:Any] {
guard let headers = request.allHTTPHeaderFields else { return [:] }
return headers.mapValues { value in
switch value.lowercased() {
case "true": return true
case "false": return false
default: return value
}
}
}
private static func encrypt(_ request: TSRequest, forSnode snode: LokiAPITarget) -> Promise<URLRequest> {
let (promise, seal) = Promise<URLRequest>.pending()
let headers = getCanonicalHeaders(for: request)
workQueue.async {
guard let snodeHexEncodedPublicKeySet = snode.publicKeySet else { return seal.reject(Error.snodePublicKeySetMissing) }
let snodeEncryptionKey = Data(hex: snodeHexEncodedPublicKeySet.encryptionKey)
let ephemeralKeyPair = Curve25519.generateKeyPair()
let uncheckedSymmetricKey = try? Curve25519.generateSharedSecret(fromPublicKey: snodeEncryptionKey, privateKey: ephemeralKeyPair.privateKey)
guard let symmetricKey = uncheckedSymmetricKey else { return seal.reject(Error.symmetricKeyGenerationFailed) }
let url = "\(snode.address):\(snode.port)/onion_req"
guard let parametersAsData = try? JSONSerialization.data(withJSONObject: request.parameters, options: []) else { return seal.reject(Error.jsonSerializationFailed) }
let onionRequestParameters: JSON = [
"method" : request.httpMethod,
"body" : String(bytes: parametersAsData, encoding: .utf8),
"headers" : headers
]
guard let onionRequestParametersAsData = try? JSONSerialization.data(withJSONObject: onionRequestParameters, options: []) else { return seal.reject(Error.jsonSerializationFailed) }
guard let ivAndCipherText = try? DiffieHellman.encrypt(onionRequestParametersAsData, using: symmetricKey) else { return seal.reject(Error.encryptionFailed) }
let onionRequestHeaders = [
"X-Sender-Public-Key" : ephemeralKeyPair.publicKey.toHexString(),
"X-Target-Snode-Key" : snodeHexEncodedPublicKeySet.idKey
]
let onionRequest = AFHTTPRequestSerializer().request(withMethod: "POST", urlString: url, parameters: nil, error: nil) as URLRequest
seal.fulfill(onionRequest)
}
return promise
}
private static func encrypt(_ request: TSRequest, forTargetSnode snode: LokiAPITarget) -> Promise<TSRequest> {
return Promise<TSRequest> { $0.fulfill(request) }
}

Loading…
Cancel
Save