summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadRustRuntime/PostQuantumKeyExchangeActor.swift
diff options
context:
space:
mode:
authorBug Magnet <marco.nikic@mullvad.net>2024-06-24 13:21:40 +0200
committerEmīls <emils@mullvad.net>2024-07-17 11:48:13 +0200
commit2df280fbf02fa1b39efffb0b27b297d39d6369c0 (patch)
tree1b6a657bb8c594e82a50bd4dfcbfb85d34395fa1 /ios/MullvadRustRuntime/PostQuantumKeyExchangeActor.swift
parentb6fe08388dcbfbe1fb54a1c89322c329be2f54f9 (diff)
downloadmullvadvpn-2df280fbf02fa1b39efffb0b27b297d39d6369c0.tar.xz
mullvadvpn-2df280fbf02fa1b39efffb0b27b297d39d6369c0.zip
Add a Rust FFI, Disable sandboxing for scripts
Diffstat (limited to 'ios/MullvadRustRuntime/PostQuantumKeyExchangeActor.swift')
-rw-r--r--ios/MullvadRustRuntime/PostQuantumKeyExchangeActor.swift137
1 files changed, 137 insertions, 0 deletions
diff --git a/ios/MullvadRustRuntime/PostQuantumKeyExchangeActor.swift b/ios/MullvadRustRuntime/PostQuantumKeyExchangeActor.swift
new file mode 100644
index 0000000000..8028c1ba3e
--- /dev/null
+++ b/ios/MullvadRustRuntime/PostQuantumKeyExchangeActor.swift
@@ -0,0 +1,137 @@
+//
+// PostQuantumKeyExchangeActor.swift
+// PacketTunnel
+//
+// Created by Marco Nikic on 2024-04-12.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import MullvadRustRuntimeProxy
+import MullvadTypes
+import NetworkExtension
+import WireGuardKitTypes
+
+public class PostQuantumKeyExchangeActor {
+ struct Negotiation {
+ var negotiator: PostQuantumKeyNegotiating
+ var inTunnelTCPConnection: NWTCPConnection
+ var tcpConnectionObserver: NSKeyValueObservation
+
+ func cancel() {
+ negotiator.cancelKeyNegotiation()
+ tcpConnectionObserver.invalidate()
+ inTunnelTCPConnection.cancel()
+ }
+ }
+
+ unowned let packetTunnel: any TunnelProvider
+ internal var negotiation: Negotiation?
+ private var timer: DispatchSourceTimer?
+ private var keyExchangeRetriesIterator: AnyIterator<Duration>!
+ private let iteratorProvider: () -> AnyIterator<Duration>
+ private let negotiationProvider: PostQuantumKeyNegotiating.Type
+
+ // Callback in the event of the negotiation failing on startup
+ var onFailure: () -> Void
+
+ public init(
+ packetTunnel: any TunnelProvider,
+ onFailure: @escaping (() -> Void),
+ negotiationProvider: PostQuantumKeyNegotiating.Type = PostQuantumKeyNegotiator.self,
+ iteratorProvider: @escaping () -> AnyIterator<Duration>
+ ) {
+ self.packetTunnel = packetTunnel
+ self.onFailure = onFailure
+ self.negotiationProvider = negotiationProvider
+ self.iteratorProvider = iteratorProvider
+ self.keyExchangeRetriesIterator = iteratorProvider()
+ }
+
+ private func createTCPConnection(_ gatewayEndpoint: NWHostEndpoint) -> NWTCPConnection {
+ self.packetTunnel.createTCPConnectionThroughTunnel(
+ to: gatewayEndpoint,
+ enableTLS: false,
+ tlsParameters: nil,
+ delegate: nil
+ )
+ }
+
+ /// Starts a new key exchange.
+ ///
+ /// Any ongoing key negotiation is stopped before starting a new one.
+ /// An exponential backoff timer is used to stop the exchange if it takes too long,
+ /// or if the TCP connection takes too long to become ready.
+ /// It is reset after every successful key exchange.
+ ///
+ /// - Parameter privateKey: The device's current private key
+ public func startNegotiation(with privateKey: PrivateKey) {
+ endCurrentNegotiation()
+ let negotiator = negotiationProvider.init()
+
+ let gatewayAddress = "10.64.0.1"
+ let IPv4Gateway = IPv4Address(gatewayAddress)!
+ let endpoint = NWHostEndpoint(hostname: gatewayAddress, port: "\(CONFIG_SERVICE_PORT)")
+ let inTunnelTCPConnection = createTCPConnection(endpoint)
+
+ // This will become the new private key of the device
+ let ephemeralSharedKey = PrivateKey()
+
+ let tcpConnectionTimeout = keyExchangeRetriesIterator.next() ?? .seconds(10)
+ // If the connection never becomes viable, force a reconnection after 10 seconds
+ scheduleInTunnelConnectionTimeout(startTime: .now() + tcpConnectionTimeout)
+
+ let tcpConnectionObserver = inTunnelTCPConnection.observe(\.isViable, options: [
+ .initial,
+ .new,
+ ]) { [weak self] observedConnection, _ in
+ guard let self, observedConnection.isViable else { return }
+ self.negotiation?.tcpConnectionObserver.invalidate()
+ self.timer?.cancel()
+
+ if !negotiator.startNegotiation(
+ gatewayIP: IPv4Gateway,
+ devicePublicKey: privateKey.publicKey,
+ presharedKey: ephemeralSharedKey,
+ postQuantumKeyReceiver: packetTunnel,
+ tcpConnection: inTunnelTCPConnection,
+ postQuantumKeyExchangeTimeout: tcpConnectionTimeout
+ ) {
+ self.negotiation = nil
+ self.onFailure()
+ }
+ }
+ negotiation = Negotiation(
+ negotiator: negotiator,
+ inTunnelTCPConnection: inTunnelTCPConnection,
+ tcpConnectionObserver: tcpConnectionObserver
+ )
+ }
+
+ /// Cancels the ongoing key exchange.
+ public func endCurrentNegotiation() {
+ negotiation?.cancel()
+ negotiation = nil
+ }
+
+ /// Resets the exponential timeout for successful key exchanges, and ends the current key exchange.
+ public func reset() {
+ keyExchangeRetriesIterator = iteratorProvider()
+ endCurrentNegotiation()
+ }
+
+ private func scheduleInTunnelConnectionTimeout(startTime: DispatchWallTime) {
+ let newTimer = DispatchSource.makeTimerSource()
+
+ newTimer.setEventHandler { [weak self] in
+ self?.onFailure()
+ self?.timer?.cancel()
+ }
+
+ newTimer.schedule(wallDeadline: startTime)
+ newTimer.activate()
+
+ timer?.cancel()
+ timer = newTimer
+ }
+}