Implement onion request decryption

pull/148/head
gmbnt 5 years ago
parent 4e00d30d05
commit 245033dbc4

@ -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)

@ -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<Any> {
@ -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<OnionBuildingResult> {
var guardSnode: LokiAPITarget!
var encryptionResult: EncryptionResult!
var symmetricKey: Data!
return getPath().then(on: workQueue) { path -> Promise<EncryptionResult> in
guardSnode = path.first!
return encrypt(payload, forTargetSnode: snode).then(on: workQueue) { r -> Promise<EncryptionResult> in
encryptionResult = r
symmetricKey = r.symmetricKey
var path = path
var rhs = snode
func addLayer() -> Promise<EncryptionResult> {
@ -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..<Int(ivSize)]
let ciphertext = ivAndCiphertext[Int(ivSize)..<ivAndCiphertext.endIndex]
do {
let gcm = GCM(iv: iv.bytes, tagLength: Int(gcmTagLength), mode: .combined)
let aes = try AES(key: symmetricKey.bytes, blockMode: gcm, padding: .pkcs7)
let result = try aes.decrypt(ciphertext.bytes)
seal.fulfill(result)
} catch (let error) {
seal.reject(error)
}
}.catch(on: workQueue) { error in
seal.reject(error)
}

Loading…
Cancel
Save