session-ios/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI+Encryption.swift

73 lines
4.1 KiB
Swift
Raw Normal View History

2020-03-31 05:52:56 +02:00
import CryptoSwift
import PromiseKit
extension OnionRequestAPI {
2020-10-06 07:52:31 +02:00
internal static func encode(ciphertext: Data, json: JSON) throws -> Data {
// The encoding of V2 onion requests looks like: | 4 bytes: size N of ciphertext | N bytes: ciphertext | json as utf8 |
guard JSONSerialization.isValidJSONObject(json) else { throw HTTP.Error.invalidJSON }
let jsonAsData = try JSONSerialization.data(withJSONObject: json, options: [ .fragmentsAllowed ])
2020-10-08 05:50:57 +02:00
let ciphertextSize = Int32(ciphertext.count).littleEndian
2020-10-07 06:39:34 +02:00
let ciphertextSizeAsData = withUnsafePointer(to: ciphertextSize) { Data(bytes: $0, count: MemoryLayout<Int32>.size) }
2020-10-06 07:52:31 +02:00
return ciphertextSizeAsData + ciphertext + jsonAsData
}
2020-07-23 04:03:39 +02:00
/// Encrypts `payload` for `destination` and returns the result. Use this to build the core of an onion request.
internal static func encrypt(_ payload: JSON, for destination: Destination) -> Promise<EncryptionResult> {
2020-03-31 05:52:56 +02:00
let (promise, seal) = Promise<EncryptionResult>.pending()
2020-05-07 03:57:40 +02:00
DispatchQueue.global(qos: .userInitiated).async {
2020-03-31 05:52:56 +02:00
do {
guard JSONSerialization.isValidJSONObject(payload) else { return seal.reject(HTTP.Error.invalidJSON) }
2020-07-23 04:03:39 +02:00
// Wrapping isn't needed for file server or open group onion requests
switch destination {
case .snode(let snode):
guard let snodeX25519PublicKey = snode.publicKeySet?.x25519Key else { return seal.reject(Error.snodePublicKeySetMissing) }
let payloadAsData = try JSONSerialization.data(withJSONObject: payload, options: [ .fragmentsAllowed ])
2020-10-07 06:39:34 +02:00
let plaintext = try encode(ciphertext: payloadAsData, json: [ "headers" : "" ])
2020-09-14 07:16:51 +02:00
let result = try EncryptionUtilities.encrypt(plaintext, using: snodeX25519PublicKey)
seal.fulfill(result)
2020-07-23 04:03:39 +02:00
case .server(_, let serverX25519PublicKey):
let plaintext = try JSONSerialization.data(withJSONObject: payload, options: [ .fragmentsAllowed ])
2020-09-14 07:16:51 +02:00
let result = try EncryptionUtilities.encrypt(plaintext, using: serverX25519PublicKey)
seal.fulfill(result)
}
2020-03-31 05:52:56 +02:00
} catch (let error) {
seal.reject(error)
}
}
return promise
}
2020-04-02 00:53:03 +02:00
/// Encrypts the previous encryption result (i.e. that of the hop after this one) for this hop. Use this to build the layers of an onion request.
2020-07-23 04:03:39 +02:00
internal static func encryptHop(from lhs: Destination, to rhs: Destination, using previousEncryptionResult: EncryptionResult) -> Promise<EncryptionResult> {
2020-03-31 05:52:56 +02:00
let (promise, seal) = Promise<EncryptionResult>.pending()
2020-05-07 03:57:40 +02:00
DispatchQueue.global(qos: .userInitiated).async {
2020-07-23 04:03:39 +02:00
var parameters: JSON
switch rhs {
case .snode(let snode):
guard let snodeED25519PublicKey = snode.publicKeySet?.ed25519Key else { return seal.reject(Error.snodePublicKeySetMissing) }
parameters = [ "destination" : snodeED25519PublicKey ]
case .server(let host, _):
2020-10-21 05:30:55 +02:00
parameters = [ "host" : host, "target" : "/loki/v2/lsrpc", "method" : "POST" ]
2020-07-23 04:03:39 +02:00
}
parameters["ephemeral_key"] = previousEncryptionResult.ephemeralPublicKey.toHexString()
2020-07-23 04:03:39 +02:00
let x25519PublicKey: String
switch lhs {
case .snode(let snode):
guard let snodeX25519PublicKey = snode.publicKeySet?.x25519Key else { return seal.reject(Error.snodePublicKeySetMissing) }
x25519PublicKey = snodeX25519PublicKey
case .server(_, let serverX25519PublicKey):
x25519PublicKey = serverX25519PublicKey
}
2020-03-31 05:52:56 +02:00
do {
2020-10-06 07:52:31 +02:00
let plaintext = try encode(ciphertext: previousEncryptionResult.ciphertext, json: parameters)
2020-09-14 07:16:51 +02:00
let result = try EncryptionUtilities.encrypt(plaintext, using: x25519PublicKey)
2020-03-31 05:52:56 +02:00
seal.fulfill(result)
} catch (let error) {
seal.reject(error)
}
}
return promise
}
}