From 245033dbc4a95f20edf4ef4fa169d87bc47deb2a Mon Sep 17 00:00:00 2001 From: gmbnt Date: Wed, 1 Apr 2020 13:32:46 +1100 Subject: [PATCH] Implement onion request decryption --- .../OnionRequestAPI+Encryption.swift | 4 ++-- .../API/Onion Requests/OnionRequestAPI.swift | 23 +++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI+Encryption.swift b/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI+Encryption.swift index 0245026f7..35af7a475 100644 --- a/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI+Encryption.swift +++ b/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI+Encryption.swift @@ -2,6 +2,8 @@ import CryptoSwift import PromiseKit extension OnionRequestAPI { + internal static let gcmTagLength: UInt = 128 + internal static let ivSize: UInt = 12 internal typealias EncryptionResult = (ciphertext: Data, symmetricKey: Data, ephemeralPublicKey: Data) @@ -21,9 +23,7 @@ extension OnionRequestAPI { /// - Note: Sync. Don't call from the main thread. private static func encrypt(_ plaintext: Data, usingAESGCMWithSymmetricKey symmetricKey: Data) throws -> Data { guard !Thread.isMainThread else { preconditionFailure("It's illegal to call encrypt(_:usingAESGCMWithSymmetricKey:) from the main thread.") } - let ivSize: UInt = 12 let iv = try getSecureRandomData(ofSize: ivSize) - let gcmTagLength: UInt = 128 let gcm = GCM(iv: iv.bytes, tagLength: Int(gcmTagLength), mode: .combined) let aes = try AES(key: symmetricKey.bytes, blockMode: gcm, padding: .pkcs7) let ciphertext = try aes.encrypt(plaintext.bytes) diff --git a/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI.swift b/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI.swift index fad380692..2248cc37a 100644 --- a/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI.swift +++ b/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI.swift @@ -1,3 +1,4 @@ +import CryptoSwift import PromiseKit // TODO: Test path snodes as well @@ -66,7 +67,7 @@ internal enum OnionRequestAPI { internal typealias Path = [LokiAPITarget] // MARK: Onion Building Result - private typealias OnionBuildingResult = (guardSnode: LokiAPITarget, encryptionResult: EncryptionResult) + private typealias OnionBuildingResult = (guardSnode: LokiAPITarget, encryptionResult: EncryptionResult, symmetricKey: Data) // MARK: Private API private static func execute(_ verb: HTTPVerb, _ url: String, parameters: JSON? = nil, timeout: TimeInterval = OnionRequestAPI.timeout) -> Promise { @@ -113,7 +114,7 @@ internal enum OnionRequestAPI { print("[Loki] [Onion Request API] Couldn't parse JSON returned by \(verb.rawValue) request to \(url).") return seal.reject(Error.invalidJSON) } - seal.fulfill(json) + seal.fulfill(json!) } task.resume() } @@ -219,10 +220,12 @@ internal enum OnionRequestAPI { private static func buildOnion(around payload: Data, targetedAt snode: LokiAPITarget) -> Promise { var guardSnode: LokiAPITarget! var encryptionResult: EncryptionResult! + var symmetricKey: Data! return getPath().then(on: workQueue) { path -> Promise in guardSnode = path.first! return encrypt(payload, forTargetSnode: snode).then(on: workQueue) { r -> Promise in encryptionResult = r + symmetricKey = r.symmetricKey var path = path var rhs = snode func addLayer() -> Promise { @@ -239,7 +242,7 @@ internal enum OnionRequestAPI { } return addLayer() } - }.map(on: workQueue) { _ in (guardSnode: guardSnode, encryptionResult: encryptionResult) } + }.map(on: workQueue) { _ in (guardSnode: guardSnode, encryptionResult: encryptionResult, symmetricKey: symmetricKey) } } // MARK: Internal API @@ -260,12 +263,24 @@ internal enum OnionRequestAPI { let url = "\(guardSnode.address):\(guardSnode.port)/onion_req" let encryptionResult = intermediate.encryptionResult let onion = encryptionResult.ciphertext + let symmetricKey = intermediate.symmetricKey let parameters: JSON = [ "ciphertext" : onion.base64EncodedString(), "ephemeral_key" : encryptionResult.ephemeralPublicKey.toHexString() ] execute(.post, url, parameters: parameters).done(on: workQueue) { rawResponse in - seal.fulfill(rawResponse) + guard let json = rawResponse as? JSON, let base64EncodedIVAndCiphertext = json["result"] as? String, + let ivAndCiphertext = Data(base64Encoded: base64EncodedIVAndCiphertext) else { return seal.reject(Error.invalidJSON) } + let iv = ivAndCiphertext[0..