summaryrefslogtreecommitdiffhomepage
path: root/ios
diff options
context:
space:
mode:
authorEmīls <emils@mullvad.net>2024-12-13 16:50:45 +0100
committerEmīls <emils@mullvad.net>2024-12-19 17:05:28 +0100
commitf1df6d52b322f19d15abf784d6ea66b02b7f501b (patch)
tree3f99a52879dc3ea3b3f7260e06aeebe037543d50 /ios
parentb3229881746dcc6c44db0489745f7a9f395b9eae (diff)
downloadmullvadvpn-f1df6d52b322f19d15abf784d6ea66b02b7f501b.tar.xz
mullvadvpn-f1df6d52b322f19d15abf784d6ea66b02b7f501b.zip
Use IAN TCP connection for ephemeral peer exchange
Diffstat (limited to 'ios')
-rw-r--r--ios/MullvadRustRuntime/EphemeralPeerExchangeActor.swift89
-rw-r--r--ios/MullvadRustRuntime/EphemeralPeerNegotiator.swift40
-rw-r--r--ios/MullvadRustRuntime/EphemeralPeerReceiver.swift52
-rw-r--r--ios/MullvadRustRuntime/PacketTunnelProvider+TCPConnection.swift118
-rw-r--r--ios/MullvadRustRuntime/include/mullvad_rust_runtime.h86
-rw-r--r--ios/MullvadRustRuntimeTests/MullvadPostQuantum+Stubs.swift37
-rw-r--r--ios/MullvadTypes/Promise.swift16
-rw-r--r--ios/MullvadTypes/Protocols/EphemeralPeerReceiver.swift33
-rw-r--r--ios/MullvadTypes/Protocols/TunnelProvider.swift27
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj17
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift6
-rw-r--r--ios/PacketTunnel/PostQuantum/MultiHopEphemeralPeerExchanger.swift32
-rw-r--r--ios/PacketTunnel/WireGuardAdapter/WgAdapter.swift17
-rw-r--r--ios/PacketTunnelCore/Actor/PacketTunnelActor+Public.swift8
-rw-r--r--ios/PacketTunnelCore/Actor/PacketTunnelActor.swift3
-rw-r--r--ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift3
-rw-r--r--ios/PacketTunnelCore/Actor/PacketTunnelActorReducer.swift9
-rw-r--r--ios/PacketTunnelCoreTests/EphemeralPeerExchangingPipelineTests.swift24
-rw-r--r--ios/PacketTunnelCoreTests/Mocks/EphemeralPeerExchangeActorStub.swift4
-rw-r--r--ios/PacketTunnelCoreTests/Mocks/KeyExchangingResultStub.swift12
-rw-r--r--ios/PacketTunnelCoreTests/MultiHopEphemeralPeerExchangerTests.swift16
-rw-r--r--ios/PacketTunnelCoreTests/SingleHopEphemeralPeerExchangerTests.swift16
22 files changed, 291 insertions, 374 deletions
diff --git a/ios/MullvadRustRuntime/EphemeralPeerExchangeActor.swift b/ios/MullvadRustRuntime/EphemeralPeerExchangeActor.swift
index 397b656d61..3f38ccb762 100644
--- a/ios/MullvadRustRuntime/EphemeralPeerExchangeActor.swift
+++ b/ios/MullvadRustRuntime/EphemeralPeerExchangeActor.swift
@@ -21,13 +21,9 @@ public protocol EphemeralPeerExchangeActorProtocol {
public class EphemeralPeerExchangeActor: EphemeralPeerExchangeActorProtocol {
struct Negotiation {
var negotiator: EphemeralPeerNegotiating
- var inTunnelTCPConnection: NWTCPConnection
- var tcpConnectionObserver: NSKeyValueObservation
func cancel() {
negotiator.cancelKeyNegotiation()
- tcpConnectionObserver.invalidate()
- inTunnelTCPConnection.cancel()
}
}
@@ -54,15 +50,6 @@ public class EphemeralPeerExchangeActor: EphemeralPeerExchangeActorProtocol {
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.
@@ -75,49 +62,46 @@ public class EphemeralPeerExchangeActor: EphemeralPeerExchangeActorProtocol {
endCurrentNegotiation()
let negotiator = negotiationProvider.init()
- let gatewayAddress = LocalNetworkIPs.gatewayAddress.rawValue
- 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()
+ let peerParameters = EphemeralPeerParameters(
+ peer_exchange_timeout: UInt64(tcpConnectionTimeout.timeInterval),
+ enable_post_quantum: enablePostQuantum,
+ enable_daita: enableDaita,
+ funcs: mapWgFunctions(functions: packetTunnel.wgFunctions())
+ )
- if !negotiator.startNegotiation(
- gatewayIP: IPv4Gateway,
- devicePublicKey: privateKey.publicKey,
- presharedKey: ephemeralSharedKey,
- peerReceiver: packetTunnel,
- tcpConnection: inTunnelTCPConnection,
- peerExchangeTimeout: tcpConnectionTimeout,
- enablePostQuantum: enablePostQuantum,
- enableDaita: enableDaita
- ) {
- // Cancel the negotiation to shut down any remaining use of the TCP connection on the Rust side
- self.negotiation?.cancel()
- self.negotiation = nil
- self.onFailure()
- }
+ if !negotiator.startNegotiation(
+ devicePublicKey: privateKey.publicKey,
+ presharedKey: ephemeralSharedKey,
+ peerReceiver: packetTunnel,
+ ephemeralPeerParams: peerParameters
+ ) {
+ // Cancel the negotiation to shut down any remaining use of the TCP connection on the Rust side
+ self.negotiation?.cancel()
+ self.negotiation = nil
+ self.onFailure()
}
+
negotiation = Negotiation(
- negotiator: negotiator,
- inTunnelTCPConnection: inTunnelTCPConnection,
- tcpConnectionObserver: tcpConnectionObserver
+ negotiator: negotiator
)
}
+ private func mapWgFunctions(functions: WgFunctionPointers) -> WgTcpConnectionFunctions {
+ var mappedFunctions = WgTcpConnectionFunctions()
+
+ mappedFunctions.close_fn = functions.close
+ mappedFunctions.open_fn = functions.open
+ mappedFunctions.send_fn = functions.send
+ mappedFunctions.recv_fn = functions.receive
+
+ return mappedFunctions
+ }
+
/// Cancels the ongoing key exchange.
public func endCurrentNegotiation() {
negotiation?.cancel()
@@ -129,19 +113,4 @@ public class EphemeralPeerExchangeActor: EphemeralPeerExchangeActorProtocol {
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
- }
}
diff --git a/ios/MullvadRustRuntime/EphemeralPeerNegotiator.swift b/ios/MullvadRustRuntime/EphemeralPeerNegotiator.swift
index ffc0dc15b3..8346b2686d 100644
--- a/ios/MullvadRustRuntime/EphemeralPeerNegotiator.swift
+++ b/ios/MullvadRustRuntime/EphemeralPeerNegotiator.swift
@@ -14,14 +14,10 @@ import WireGuardKitTypes
// swiftlint:disable function_parameter_count
public protocol EphemeralPeerNegotiating {
func startNegotiation(
- gatewayIP: IPv4Address,
devicePublicKey: PublicKey,
presharedKey: PrivateKey,
peerReceiver: any TunnelProvider,
- tcpConnection: NWTCPConnection,
- peerExchangeTimeout: Duration,
- enablePostQuantum: Bool,
- enableDaita: Bool
+ ephemeralPeerParams: EphemeralPeerParameters
) -> Bool
func cancelKeyNegotiation()
@@ -33,35 +29,30 @@ public protocol EphemeralPeerNegotiating {
public class EphemeralPeerNegotiator: EphemeralPeerNegotiating {
required public init() {}
- var cancelToken: EphemeralPeerCancelToken?
+ var cancelToken: OpaquePointer?
public func startNegotiation(
- gatewayIP: IPv4Address,
devicePublicKey: PublicKey,
presharedKey: PrivateKey,
peerReceiver: any TunnelProvider,
- tcpConnection: NWTCPConnection,
- peerExchangeTimeout: Duration,
- enablePostQuantum: Bool,
- enableDaita: Bool
+ ephemeralPeerParams: EphemeralPeerParameters
) -> Bool {
// swiftlint:disable:next force_cast
let ephemeralPeerReceiver = Unmanaged.passUnretained(peerReceiver as! EphemeralPeerReceiver)
.toOpaque()
- let opaqueConnection = Unmanaged.passUnretained(tcpConnection).toOpaque()
- var cancelToken = EphemeralPeerCancelToken()
- let result = request_ephemeral_peer(
+ guard let tunnelHandle = try? peerReceiver.tunnelHandle() else {
+ return false
+ }
+
+ let cancelToken = request_ephemeral_peer(
devicePublicKey.rawValue.map { $0 },
presharedKey.rawValue.map { $0 },
ephemeralPeerReceiver,
- opaqueConnection,
- &cancelToken,
- UInt64(peerExchangeTimeout.timeInterval),
- enablePostQuantum,
- enableDaita
+ tunnelHandle,
+ ephemeralPeerParams
)
- guard result == 0 else {
+ guard let cancelToken else {
return false
}
self.cancelToken = cancelToken
@@ -69,13 +60,14 @@ public class EphemeralPeerNegotiator: EphemeralPeerNegotiating {
}
public func cancelKeyNegotiation() {
- guard var cancelToken else { return }
- cancel_ephemeral_peer_exchange(&cancelToken)
+ guard let cancelToken else { return }
+ cancel_ephemeral_peer_exchange(cancelToken)
+ self.cancelToken = nil
}
deinit {
- guard var cancelToken else { return }
- drop_ephemeral_peer_exchange_token(&cancelToken)
+ guard let cancelToken else { return }
+ drop_ephemeral_peer_exchange_token(cancelToken)
}
}
diff --git a/ios/MullvadRustRuntime/EphemeralPeerReceiver.swift b/ios/MullvadRustRuntime/EphemeralPeerReceiver.swift
new file mode 100644
index 0000000000..2b1b4adac5
--- /dev/null
+++ b/ios/MullvadRustRuntime/EphemeralPeerReceiver.swift
@@ -0,0 +1,52 @@
+//
+// EphemeralPeerReceiver.swift
+// PacketTunnel
+//
+// Created by Marco Nikic on 2024-02-15.
+// Copyright © 2023 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import MullvadRustRuntimeProxy
+import MullvadTypes
+import NetworkExtension
+import WireGuardKitTypes
+
+/// End sequence of an ephemeral peer exchange.
+///
+/// This FFI function is called by Rust when an ephemeral peer negotiation succeeded or failed.
+/// When both the `rawPresharedKey` and the `rawEphemeralKey` are raw pointers to 32 bytes data arrays,
+/// the quantum-secure key exchange is considered successful.
+/// If the `rawPresharedKey` is nil, but there is a valid `rawEphemeralKey`, it means a Daita peer has been negotiated with.
+/// If `rawEphemeralKey` is nil, the negotiation is considered failed.
+///
+/// - Parameters:
+/// - rawEphemeralPeerReceiver: A raw pointer to the running instance of `NEPacketTunnelProvider`
+/// - rawPresharedKey: A raw pointer to the quantum-secure pre shared key
+/// - rawEphemeralKey: A raw pointer to the ephemeral private key of the device
+@_cdecl("swift_ephemeral_peer_ready")
+func receivePostQuantumKey(
+ rawEphemeralPeerReceiver: UnsafeMutableRawPointer?,
+ rawPresharedKey: UnsafeMutableRawPointer?,
+ rawEphemeralKey: UnsafeMutableRawPointer?
+) {
+ guard let rawEphemeralPeerReceiver else { return }
+ let ephemeralPeerReceiver = Unmanaged<EphemeralPeerReceiver>.fromOpaque(rawEphemeralPeerReceiver)
+ .takeUnretainedValue()
+
+ // If there are no private keys for the ephemeral peer, then the negotiation either failed, or timed out.
+ guard let rawEphemeralKey,
+ let ephemeralKey = PrivateKey(rawValue: Data(bytes: rawEphemeralKey, count: 32)) else {
+ ephemeralPeerReceiver.ephemeralPeerExchangeFailed()
+ return
+ }
+
+ // If there is a pre-shared key, an ephemeral peer was negotiated with Post Quantum options
+ // Otherwise, a Daita enabled ephemeral peer was requested
+ if let rawPresharedKey, let key = PreSharedKey(rawValue: Data(bytes: rawPresharedKey, count: 32)) {
+ ephemeralPeerReceiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey)
+ } else {
+ ephemeralPeerReceiver.receiveEphemeralPeerPrivateKey(ephemeralKey)
+ }
+ return
+}
diff --git a/ios/MullvadRustRuntime/PacketTunnelProvider+TCPConnection.swift b/ios/MullvadRustRuntime/PacketTunnelProvider+TCPConnection.swift
deleted file mode 100644
index e19750d4dc..0000000000
--- a/ios/MullvadRustRuntime/PacketTunnelProvider+TCPConnection.swift
+++ /dev/null
@@ -1,118 +0,0 @@
-//
-// PacketTunnelProvider+TCPConnection.swift
-// PacketTunnel
-//
-// Created by Marco Nikic on 2024-02-15.
-// Copyright © 2023 Mullvad VPN AB. All rights reserved.
-//
-
-import Foundation
-import MullvadRustRuntimeProxy
-import MullvadTypes
-import NetworkExtension
-import WireGuardKitTypes
-
-/// Writes data to the in-tunnel TCP connection
-///
-/// This FFI function is called by Rust whenever there is data to be written to the in-tunnel TCP connection when exchanging
-/// quantum-resistant pre shared keys.
-///
-/// Whenever the flow control is given back from the connection, acknowledge that data was written using `rawWriteAcknowledgement`.
-/// - Parameters:
-/// - rawConnection: A raw pointer to the in-tunnel TCP connection
-/// - rawData: A raw pointer to the data to write in the connection
-/// - dataLength: The length of data to write in the connection
-/// - rawWriteAcknowledgement: An opaque pointer needed for write acknowledgement
-@_cdecl("swift_nw_tcp_connection_send")
-func tcpConnectionSend(
- rawConnection: UnsafeMutableRawPointer?,
- rawData: UnsafeMutableRawPointer,
- dataLength: UInt,
- rawWriteAcknowledgement: UnsafeMutableRawPointer?
-) {
- guard let rawConnection, let rawWriteAcknowledgement else {
- handle_sent(0, rawWriteAcknowledgement)
- return
- }
- let tcpConnection = Unmanaged<NWTCPConnection>.fromOpaque(rawConnection).takeUnretainedValue()
- let data = Data(bytes: rawData, count: Int(dataLength))
-
- // The guarantee that all writes are sequential is done by virtue of not returning the execution context
- // to Rust before this closure is done executing.
- tcpConnection.write(data, completionHandler: { maybeError in
- if maybeError != nil {
- handle_sent(0, rawWriteAcknowledgement)
- } else {
- handle_sent(dataLength, rawWriteAcknowledgement)
- }
- })
-}
-
-/// Reads data to the in-tunnel TCP connection
-///
-/// This FFI function is called by Rust whenever there is data to be read from the in-tunnel TCP connection when exchanging
-/// quantum-resistant pre shared keys.
-///
-/// Whenever the flow control is given back from the connection, acknowledge that data was read using `rawReadAcknowledgement`.
-/// - Parameters:
-/// - rawConnection: A raw pointer to the in-tunnel TCP connection
-/// - rawReadAcknowledgement: An opaque pointer needed for read acknowledgement
-@_cdecl("swift_nw_tcp_connection_read")
-func tcpConnectionReceive(
- rawConnection: UnsafeMutableRawPointer?,
- rawReadAcknowledgement: UnsafeMutableRawPointer?
-) {
- guard let rawConnection, let rawReadAcknowledgement else {
- handle_recv(nil, 0, rawReadAcknowledgement)
- return
- }
- let tcpConnection = Unmanaged<NWTCPConnection>.fromOpaque(rawConnection).takeUnretainedValue()
- tcpConnection.readMinimumLength(0, maximumLength: Int(UInt16.max)) { data, maybeError in
- if let data {
- if maybeError != nil {
- handle_recv(nil, 0, rawReadAcknowledgement)
- } else {
- handle_recv(data.map { $0 }, UInt(data.count), rawReadAcknowledgement)
- }
- }
- }
-}
-
-/// End sequence of an ephemeral peer exchange.
-///
-/// This FFI function is called by Rust when an ephemeral peer negotiation succeeded or failed.
-/// When both the `rawPresharedKey` and the `rawEphemeralKey` are raw pointers to 32 bytes data arrays,
-/// the quantum-secure key exchange is considered successful.
-/// If the `rawPresharedKey` is nil, but there is a valid `rawEphemeralKey`, it means a Daita peer has been negotiated with.
-/// If `rawEphemeralKey` is nil, the negotiation is considered failed.
-///
-/// - Parameters:
-/// - rawEphemeralPeerReceiver: A raw pointer to the running instance of `NEPacketTunnelProvider`
-/// - rawPresharedKey: A raw pointer to the quantum-secure pre shared key
-/// - rawEphemeralKey: A raw pointer to the ephemeral private key of the device
-@_cdecl("swift_ephemeral_peer_ready")
-func receivePostQuantumKey(
- rawEphemeralPeerReceiver: UnsafeMutableRawPointer?,
- rawPresharedKey: UnsafeMutableRawPointer?,
- rawEphemeralKey: UnsafeMutableRawPointer?
-) {
- guard let rawEphemeralPeerReceiver else { return }
- let ephemeralPeerReceiver = Unmanaged<EphemeralPeerReceiver>.fromOpaque(rawEphemeralPeerReceiver)
- .takeUnretainedValue()
-
- // If there are no private keys for the ephemeral peer, then the negotiation either failed, or timed out.
- guard let rawEphemeralKey,
- let ephemeralKey = PrivateKey(rawValue: Data(bytes: rawEphemeralKey, count: 32)) else {
- ephemeralPeerReceiver.ephemeralPeerExchangeFailed()
- return
- }
-
- // If there is a pre-shared key, an ephemeral peer was negotiated with Post Quantum options
- // Otherwise, a Daita enabled ephemeral peer was requested
- if let rawPresharedKey, let key = PreSharedKey(rawValue: Data(bytes: rawPresharedKey, count: 32)) {
- ephemeralPeerReceiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey)
- } else {
- ephemeralPeerReceiver.receiveEphemeralPeerPrivateKey(ephemeralKey)
- }
- return
-}
diff --git a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
index a45c6ed6c3..93c04587f1 100644
--- a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
+++ b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
@@ -20,14 +20,26 @@ typedef uint8_t TunnelObfuscatorProtocol;
*/
typedef struct EncryptedDnsProxyState EncryptedDnsProxyState;
+typedef struct ExchangeCancelToken ExchangeCancelToken;
+
typedef struct ProxyHandle {
void *context;
uint16_t port;
} ProxyHandle;
-typedef struct EphemeralPeerCancelToken {
- void *context;
-} EphemeralPeerCancelToken;
+typedef struct WgTcpConnectionFunctions {
+ int32_t (*open_fn)(int32_t tunnelHandle, const char *address, uint64_t timeout);
+ int32_t (*close_fn)(int32_t tunnelHandle, int32_t socketHandle);
+ int32_t (*recv_fn)(int32_t tunnelHandle, int32_t socketHandle, uint8_t *data, int32_t len);
+ int32_t (*send_fn)(int32_t tunnelHandle, int32_t socketHandle, const uint8_t *data, int32_t len);
+} WgTcpConnectionFunctions;
+
+typedef struct EphemeralPeerParameters {
+ uint64_t peer_exchange_timeout;
+ bool enable_post_quantum;
+ bool enable_daita;
+ struct WgTcpConnectionFunctions funcs;
+} EphemeralPeerParameters;
extern const uint16_t CONFIG_SERVICE_PORT;
@@ -84,43 +96,17 @@ int32_t encrypted_dns_proxy_stop(struct ProxyHandle *proxy_config);
* `sender` must be pointing to a valid instance of a `EphemeralPeerCancelToken` created by the
* `PacketTunnelProvider`.
*/
-void cancel_ephemeral_peer_exchange(const struct EphemeralPeerCancelToken *sender);
+void cancel_ephemeral_peer_exchange(struct ExchangeCancelToken *sender);
/**
- * Called by the Swift side to signal that the Rust `EphemeralPeerCancelToken` can be safely dropped
- * from memory.
+ * Called by the Swift side to signal that the Rust `EphemeralPeerCancelToken` can be safely
+ * dropped from memory.
*
* # Safety
* `sender` must be pointing to a valid instance of a `EphemeralPeerCancelToken` created by the
* `PacketTunnelProvider`.
*/
-void drop_ephemeral_peer_exchange_token(const struct EphemeralPeerCancelToken *sender);
-
-/**
- * Called by Swift whenever data has been written to the in-tunnel TCP connection when exchanging
- * quantum-resistant pre shared keys, or ephemeral peers.
- *
- * If `bytes_sent` is 0, this indicates that the connection was closed or that an error occurred.
- *
- * # Safety
- * `sender` must be pointing to a valid instance of a `write_tx` created by the `IosTcpProvider`
- * Callback to call when the TCP connection has written data.
- */
-void handle_sent(uintptr_t bytes_sent, const void *sender);
-
-/**
- * Called by Swift whenever data has been read from the in-tunnel TCP connection when exchanging
- * quantum-resistant pre shared keys, or ephemeral peers.
- *
- * If `data` is null or empty, this indicates that the connection was closed or that an error
- * occurred. An empty buffer is sent to the underlying reader to signal EOF.
- *
- * # Safety
- * `sender` must be pointing to a valid instance of a `read_tx` created by the `IosTcpProvider`
- *
- * Callback to call when the TCP connection has received data.
- */
-void handle_recv(const uint8_t *data, uintptr_t data_len, const void *sender);
+void drop_ephemeral_peer_exchange_token(struct ExchangeCancelToken *sender);
/**
* Entry point for requesting ephemeral peers on iOS.
@@ -128,33 +114,15 @@ void handle_recv(const uint8_t *data, uintptr_t data_len, const void *sender);
* # Safety
* `public_key` and `ephemeral_key` must be valid respective `PublicKey` and `PrivateKey` types.
* They will not be valid after this function is called, and thus must be copied here.
- * `packet_tunnel` and `tcp_connection` must be valid pointers to a packet tunnel and a TCP
- * connection instances.
- * `cancel_token` should be owned by the caller of this function.
- */
-int32_t request_ephemeral_peer(const uint8_t *public_key,
- const uint8_t *ephemeral_key,
- const void *packet_tunnel,
- const void *tcp_connection,
- struct EphemeralPeerCancelToken *cancel_token,
- uint64_t peer_exchange_timeout,
- bool enable_post_quantum,
- bool enable_daita);
-
-/**
- * Called when there is data to send on the TCP connection.
- * The TCP connection must write data on the wire, then call the `handle_sent` function.
- */
-extern void swift_nw_tcp_connection_send(const void *connection,
- const void *data,
- uintptr_t data_len,
- const void *sender);
-
-/**
- * Called when there is data to read on the TCP connection.
- * The TCP connection must read data from the wire, then call the `handle_read` function.
+ * `packet_tunnel` must be valid pointers to a packet tunnel, the packet tunnel pointer must
+ * outlive the ephemeral peer exchange. `cancel_token` should be owned by the caller of this
+ * function.
*/
-extern void swift_nw_tcp_connection_read(const void *connection, const void *sender);
+struct ExchangeCancelToken *request_ephemeral_peer(const uint8_t *public_key,
+ const uint8_t *ephemeral_key,
+ const void *packet_tunnel,
+ int32_t tunnel_handle,
+ struct EphemeralPeerParameters peer_parameters);
/**
* Called when the preshared post quantum key is ready,
diff --git a/ios/MullvadRustRuntimeTests/MullvadPostQuantum+Stubs.swift b/ios/MullvadRustRuntimeTests/MullvadPostQuantum+Stubs.swift
index 683e1ab8de..30ce49a891 100644
--- a/ios/MullvadRustRuntimeTests/MullvadPostQuantum+Stubs.swift
+++ b/ios/MullvadRustRuntimeTests/MullvadPostQuantum+Stubs.swift
@@ -27,6 +27,19 @@ class NWTCPConnectionStub: NWTCPConnection {
}
class TunnelProviderStub: TunnelProvider {
+ func tunnelHandle() throws -> Int32 {
+ 0
+ }
+
+ func wgFunctions() -> MullvadTypes.WgFuncPointers {
+ return MullvadTypes.WgFuncPointers(
+ open: { _, _, _ in return 0 },
+ close: { _, _ in return 0 },
+ receive: { _, _, _, _ in return 0 },
+ send: { _, _, _, _ in return 0 }
+ )
+ }
+
let tcpConnection: NWTCPConnectionStub
init(tcpConnection: NWTCPConnectionStub) {
@@ -55,15 +68,13 @@ class FailedNegotiatorStub: EphemeralPeerNegotiating {
}
func startNegotiation(
- gatewayIP: IPv4Address,
devicePublicKey: WireGuardKitTypes.PublicKey,
presharedKey: WireGuardKitTypes.PrivateKey,
- peerReceiver packetTunnel: any MullvadTypes.TunnelProvider,
- tcpConnection: NWTCPConnection,
- peerExchangeTimeout: MullvadTypes.Duration,
- enablePostQuantum: Bool,
- enableDaita: Bool
- ) -> Bool { false }
+ peerReceiver: any MullvadTypes.TunnelProvider,
+ ephemeralPeerParams: EphemeralPeerParameters
+ ) -> Bool {
+ false
+ }
func cancelKeyNegotiation() {
onCancelKeyNegotiation?()
@@ -81,15 +92,13 @@ class SuccessfulNegotiatorStub: EphemeralPeerNegotiating {
}
func startNegotiation(
- gatewayIP: IPv4Address,
devicePublicKey: WireGuardKitTypes.PublicKey,
presharedKey: WireGuardKitTypes.PrivateKey,
- peerReceiver packetTunnel: any MullvadTypes.TunnelProvider,
- tcpConnection: NWTCPConnection,
- peerExchangeTimeout: MullvadTypes.Duration,
- enablePostQuantum: Bool,
- enableDaita: Bool
- ) -> Bool { true }
+ peerReceiver: any MullvadTypes.TunnelProvider,
+ ephemeralPeerParams: EphemeralPeerParameters
+ ) -> Bool {
+ true
+ }
func cancelKeyNegotiation() {
onCancelKeyNegotiation?()
diff --git a/ios/MullvadTypes/Promise.swift b/ios/MullvadTypes/Promise.swift
index 886f00f633..48a12f8182 100644
--- a/ios/MullvadTypes/Promise.swift
+++ b/ios/MullvadTypes/Promise.swift
@@ -47,3 +47,19 @@ public final class Promise<Success, Failure: Error> {
}
}
}
+
+
+public struct OneshotChannel {
+ private let semaphore = DispatchSemaphore(value: 0)
+
+ public init() {
+ }
+
+ public mutating func send() {
+ semaphore.signal()
+ }
+
+ public func receive() {
+ semaphore.wait()
+ }
+}
diff --git a/ios/MullvadTypes/Protocols/EphemeralPeerReceiver.swift b/ios/MullvadTypes/Protocols/EphemeralPeerReceiver.swift
index e5fc68f68a..f7fbe8e4a1 100644
--- a/ios/MullvadTypes/Protocols/EphemeralPeerReceiver.swift
+++ b/ios/MullvadTypes/Protocols/EphemeralPeerReceiver.swift
@@ -11,10 +11,20 @@ import NetworkExtension
import WireGuardKitTypes
public class EphemeralPeerReceiver: EphemeralPeerReceiving, TunnelProvider {
- unowned let tunnelProvider: NEPacketTunnelProvider
+ public func tunnelHandle() throws -> Int32 {
+ try tunnelProvider.tunnelHandle()
+ }
+
+ public func wgFunctions() -> WgFunctionPointers {
+ tunnelProvider.wgFunctions()
+ }
+
+ unowned let tunnelProvider: any TunnelProvider
+ let keyReceiver: any EphemeralPeerReceiving
- public init(tunnelProvider: NEPacketTunnelProvider) {
+ public init(tunnelProvider: TunnelProvider, keyReceiver: any EphemeralPeerReceiving) {
self.tunnelProvider = tunnelProvider
+ self.keyReceiver = keyReceiver
}
// MARK: - EphemeralPeerReceiving
@@ -30,23 +40,6 @@ public class EphemeralPeerReceiver: EphemeralPeerReceiving, TunnelProvider {
}
public func ephemeralPeerExchangeFailed() {
- guard let receiver = tunnelProvider as? EphemeralPeerReceiving else { return }
- receiver.ephemeralPeerExchangeFailed()
- }
-
- // MARK: - TunnelProvider
-
- public func createTCPConnectionThroughTunnel(
- to remoteEndpoint: NWEndpoint,
- enableTLS: Bool,
- tlsParameters TLSParameters: NWTLSParameters?,
- delegate: Any?
- ) -> NWTCPConnection {
- tunnelProvider.createTCPConnectionThroughTunnel(
- to: remoteEndpoint,
- enableTLS: enableTLS,
- tlsParameters: TLSParameters,
- delegate: delegate
- )
+ keyReceiver.ephemeralPeerExchangeFailed()
}
}
diff --git a/ios/MullvadTypes/Protocols/TunnelProvider.swift b/ios/MullvadTypes/Protocols/TunnelProvider.swift
index 61aa99ba7d..06524bd826 100644
--- a/ios/MullvadTypes/Protocols/TunnelProvider.swift
+++ b/ios/MullvadTypes/Protocols/TunnelProvider.swift
@@ -10,12 +10,25 @@ import Foundation
import NetworkExtension
public protocol TunnelProvider: AnyObject {
- func createTCPConnectionThroughTunnel(
- to remoteEndpoint: NWEndpoint,
- enableTLS: Bool,
- tlsParameters TLSParameters: NWTLSParameters?,
- delegate: Any?
- ) -> NWTCPConnection
+ func tunnelHandle() throws -> Int32
+ func wgFunctions() -> WgFunctionPointers
}
-extension NEPacketTunnelProvider: TunnelProvider {}
+public typealias TcpOpenFunction = @convention(c) (Int32, UnsafePointer<CChar>?, UInt64) -> Int32
+public typealias TcpCloseFunction = @convention(c) (Int32, Int32) -> Int32
+public typealias TcpSendFunction = @convention(c) (Int32, Int32, UnsafePointer<UInt8>?, Int32) -> Int32
+public typealias TcpRecvFunction = @convention(c) (Int32, Int32, UnsafeMutablePointer<UInt8>?, Int32) -> Int32
+
+public struct WgFunctionPointers {
+ public let open: TcpOpenFunction
+ public let close: TcpCloseFunction
+ public let receive: TcpRecvFunction
+ public let send: TcpSendFunction
+
+ public init(open: TcpOpenFunction, close: TcpCloseFunction, receive: TcpRecvFunction, send: TcpSendFunction) {
+ self.open = open
+ self.close = close
+ self.receive = receive
+ self.send = send
+ }
+}
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 1379f20a73..31cab31228 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -659,10 +659,10 @@
7AF9BE902A39F26000DBFEDB /* Collection+Sorting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF9BE8F2A39F26000DBFEDB /* Collection+Sorting.swift */; };
7AF9BE952A40461100DBFEDB /* RelayFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF9BE942A40461100DBFEDB /* RelayFilterView.swift */; };
7AF9BE972A41C71F00DBFEDB /* ChipViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF9BE962A41C71F00DBFEDB /* ChipViewCell.swift */; };
- 7AFBE38B2D09AAFF002335FC /* SinglehopPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE38A2D09AAFF002335FC /* SinglehopPicker.swift */; };
- 7AFBE38D2D09AB2E002335FC /* MultihopPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE38C2D09AB2E002335FC /* MultihopPicker.swift */; };
7AFBE3872D084C9D002335FC /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE3862D084C96002335FC /* ActivityIndicator.swift */; };
7AFBE3892D089163002335FC /* FI_TunnelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE3882D08915D002335FC /* FI_TunnelViewController.swift */; };
+ 7AFBE38B2D09AAFF002335FC /* SinglehopPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE38A2D09AAFF002335FC /* SinglehopPicker.swift */; };
+ 7AFBE38D2D09AB2E002335FC /* MultihopPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE38C2D09AB2E002335FC /* MultihopPicker.swift */; };
850201DB2B503D7700EF8C96 /* RelayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850201DA2B503D7700EF8C96 /* RelayTests.swift */; };
850201DD2B503D8C00EF8C96 /* SelectLocationPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850201DC2B503D8C00EF8C96 /* SelectLocationPage.swift */; };
850201DF2B5040A500EF8C96 /* TunnelControlPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850201DE2B5040A500EF8C96 /* TunnelControlPage.swift */; };
@@ -729,7 +729,7 @@
A91614D12B108D1B00F416EB /* TransportLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91614D02B108D1B00F416EB /* TransportLayer.swift */; };
A91614D62B10B26B00F416EB /* TunnelControlViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91614D52B10B26B00F416EB /* TunnelControlViewModel.swift */; };
A917352129FAAA5200D5DCFD /* TransportStrategyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A917352029FAAA5200D5DCFD /* TransportStrategyTests.swift */; };
- A9173C322C36CCDD00F6A08C /* PacketTunnelProvider+TCPConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9A557F42B7E3E5C0017ADA8 /* PacketTunnelProvider+TCPConnection.swift */; };
+ A9173C322C36CCDD00F6A08C /* EphemeralPeerReceiver.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9A557F42B7E3E5C0017ADA8 /* EphemeralPeerReceiver.swift */; };
A9173C372C36CD2B00F6A08C /* MullvadTypes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223D5294C8E5E0029F5F8 /* MullvadTypes.framework */; platformFilter = ios; };
A91D78E42B03C01600FCD5D3 /* MullvadSettings.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58B2FDD32AA71D2A003EB5C6 /* MullvadSettings.framework */; };
A91EBEDA2C1337040004A84D /* RetryStrategyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EBED92C1337040004A84D /* RetryStrategyTests.swift */; };
@@ -2020,10 +2020,10 @@
7AF9BE8F2A39F26000DBFEDB /* Collection+Sorting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+Sorting.swift"; sourceTree = "<group>"; };
7AF9BE942A40461100DBFEDB /* RelayFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilterView.swift; sourceTree = "<group>"; };
7AF9BE962A41C71F00DBFEDB /* ChipViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChipViewCell.swift; sourceTree = "<group>"; };
- 7AFBE38A2D09AAFF002335FC /* SinglehopPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SinglehopPicker.swift; sourceTree = "<group>"; };
- 7AFBE38C2D09AB2E002335FC /* MultihopPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultihopPicker.swift; sourceTree = "<group>"; };
7AFBE3862D084C96002335FC /* ActivityIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = "<group>"; };
7AFBE3882D08915D002335FC /* FI_TunnelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FI_TunnelViewController.swift; sourceTree = "<group>"; };
+ 7AFBE38A2D09AAFF002335FC /* SinglehopPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SinglehopPicker.swift; sourceTree = "<group>"; };
+ 7AFBE38C2D09AB2E002335FC /* MultihopPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultihopPicker.swift; sourceTree = "<group>"; };
85006A8E2B73EF67004AD8FB /* MullvadVPNUITestsSmoke.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = MullvadVPNUITestsSmoke.xctestplan; sourceTree = "<group>"; };
850201DA2B503D7700EF8C96 /* RelayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayTests.swift; sourceTree = "<group>"; };
850201DC2B503D8C00EF8C96 /* SelectLocationPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectLocationPage.swift; sourceTree = "<group>"; };
@@ -2138,7 +2138,7 @@
A99E5EDF2B7628150033F241 /* ProblemReportViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProblemReportViewModel.swift; sourceTree = "<group>"; };
A99E5EE12B762ED30033F241 /* ProblemReportViewController+ViewManagement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProblemReportViewController+ViewManagement.swift"; sourceTree = "<group>"; };
A9A1DE782AD5708E0073F689 /* TransportStrategy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransportStrategy.swift; sourceTree = "<group>"; };
- A9A557F42B7E3E5C0017ADA8 /* PacketTunnelProvider+TCPConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PacketTunnelProvider+TCPConnection.swift"; sourceTree = "<group>"; };
+ A9A557F42B7E3E5C0017ADA8 /* EphemeralPeerReceiver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EphemeralPeerReceiver.swift; sourceTree = "<group>"; };
A9A5F9A12ACB003D0083449F /* TunnelManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelManagerTests.swift; sourceTree = "<group>"; };
A9A8A8EA2A262AB30086D569 /* FileCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileCache.swift; sourceTree = "<group>"; };
A9B6AC172ADE8F4300F7802A /* MigrationManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationManagerTests.swift; sourceTree = "<group>"; };
@@ -3928,7 +3928,6 @@
7A0EAE982D01B29E00D3EB8B /* Recovered References */ = {
isa = PBXGroup;
children = (
- 7AA1309C2D0072F900640DF9 /* View+Size.swift */,
);
name = "Recovered References";
sourceTree = "<group>";
@@ -4255,7 +4254,7 @@
children = (
A9D9A4D32C36E1EA004088DD /* mullvad_rust_runtime.h */,
A992DA1F2C24709F00DE7CE5 /* MullvadRustRuntime.h */,
- A9A557F42B7E3E5C0017ADA8 /* PacketTunnelProvider+TCPConnection.swift */,
+ A9A557F42B7E3E5C0017ADA8 /* EphemeralPeerReceiver.swift */,
A948809A2BC9308D0090A44C /* EphemeralPeerExchangeActor.swift */,
A9EB4F9C2B7FAB21002A2D7A /* EphemeralPeerNegotiator.swift */,
F0DDE40F2B220458006B57A7 /* ShadowSocksProxy.swift */,
@@ -6447,7 +6446,7 @@
014449952CA293B100C0C2F2 /* EncryptedDNSProxy.swift in Sources */,
A9D9A4BB2C36D397004088DD /* EphemeralPeerNegotiator.swift in Sources */,
A9D9A4B22C36D12D004088DD /* TunnelObfuscator.swift in Sources */,
- A9173C322C36CCDD00F6A08C /* PacketTunnelProvider+TCPConnection.swift in Sources */,
+ A9173C322C36CCDD00F6A08C /* EphemeralPeerReceiver.swift in Sources */,
F05919802C45515200C301F3 /* EphemeralPeerExchangeActor.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
index 84ff437b20..3216773eb5 100644
--- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
+++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
@@ -34,7 +34,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
private let tunnelSettingsListener = TunnelSettingsListener()
private lazy var ephemeralPeerReceiver = {
- EphemeralPeerReceiver(tunnelProvider: self)
+ EphemeralPeerReceiver(tunnelProvider: adapter, keyReceiver: self)
}()
// swiftlint:disable:next function_body_length
@@ -110,7 +110,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
iteratorProvider: { REST.RetryStrategy.postQuantumKeyExchange.makeDelayIterator() }
),
onUpdateConfiguration: { [unowned self] configuration in
- actor.changeEphemeralPeerNegotiationState(configuration: configuration)
+ let channel = OneshotChannel()
+ actor.changeEphemeralPeerNegotiationState(configuration: configuration, reconfigurationSemaphore: channel)
+ channel.receive()
}, onFinish: { [unowned self] in
actor.notifyEphemeralPeerNegotiated()
}
diff --git a/ios/PacketTunnel/PostQuantum/MultiHopEphemeralPeerExchanger.swift b/ios/PacketTunnel/PostQuantum/MultiHopEphemeralPeerExchanger.swift
index ee1a3afe6c..0e2bc97778 100644
--- a/ios/PacketTunnel/PostQuantum/MultiHopEphemeralPeerExchanger.swift
+++ b/ios/PacketTunnel/PostQuantum/MultiHopEphemeralPeerExchanger.swift
@@ -19,7 +19,7 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol {
let keyExchanger: EphemeralPeerExchangeActorProtocol
let devicePrivateKey: PrivateKey
let onFinish: () -> Void
- let onUpdateConfiguration: (EphemeralPeerNegotiationState) -> Void
+ let onUpdateConfiguration: (EphemeralPeerNegotiationState) async -> Void
let enablePostQuantum: Bool
let enableDaita: Bool
@@ -48,7 +48,7 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol {
keyExchanger: EphemeralPeerExchangeActorProtocol,
enablePostQuantum: Bool,
enableDaita: Bool,
- onUpdateConfiguration: @escaping (EphemeralPeerNegotiationState) -> Void,
+ onUpdateConfiguration: @escaping (EphemeralPeerNegotiationState) async -> Void,
onFinish: @escaping () -> Void
) {
self.entry = entry
@@ -61,37 +61,37 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol {
self.onFinish = onFinish
}
- func start() {
+ func start() async {
guard state == .initial else { return }
- negotiateWithEntry()
+ await negotiateWithEntry()
}
- public func receiveEphemeralPeerPrivateKey(_ ephemeralPeerPrivateKey: PrivateKey) {
+ public func receiveEphemeralPeerPrivateKey(_ ephemeralPeerPrivateKey: PrivateKey) async {
if state == .negotiatingWithEntry {
entryPeerKey = EphemeralPeerKey(ephemeralKey: ephemeralPeerPrivateKey)
- negotiateBetweenEntryAndExit()
+ await negotiateBetweenEntryAndExit()
} else if state == .negotiatingBetweenEntryAndExit {
exitPeerKey = EphemeralPeerKey(ephemeralKey: ephemeralPeerPrivateKey)
- makeConnection()
+ await makeConnection()
}
}
func receivePostQuantumKey(
_ preSharedKey: PreSharedKey,
ephemeralKey: PrivateKey
- ) {
+ ) async {
if state == .negotiatingWithEntry {
entryPeerKey = EphemeralPeerKey(preSharedKey: preSharedKey, ephemeralKey: ephemeralKey)
- negotiateBetweenEntryAndExit()
+ await negotiateBetweenEntryAndExit()
} else if state == .negotiatingBetweenEntryAndExit {
exitPeerKey = EphemeralPeerKey(preSharedKey: preSharedKey, ephemeralKey: ephemeralKey)
- makeConnection()
+ await makeConnection()
}
}
- private func negotiateWithEntry() {
+ private func negotiateWithEntry() async {
state = .negotiatingWithEntry
- onUpdateConfiguration(.single(EphemeralPeerRelayConfiguration(
+ await onUpdateConfiguration(.single(EphemeralPeerRelayConfiguration(
relay: entry,
configuration: EphemeralPeerConfiguration(
privateKey: devicePrivateKey,
@@ -105,9 +105,9 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol {
)
}
- private func negotiateBetweenEntryAndExit() {
+ private func negotiateBetweenEntryAndExit() async {
state = .negotiatingBetweenEntryAndExit
- onUpdateConfiguration(.multi(
+ await onUpdateConfiguration(.multi(
entry: EphemeralPeerRelayConfiguration(
relay: entry,
configuration: EphemeralPeerConfiguration(
@@ -132,9 +132,9 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol {
)
}
- private func makeConnection() {
+ private func makeConnection() async {
state = .makeConnection
- onUpdateConfiguration(.multi(
+ await onUpdateConfiguration(.multi(
entry: EphemeralPeerRelayConfiguration(
relay: entry,
configuration: EphemeralPeerConfiguration(
diff --git a/ios/PacketTunnel/WireGuardAdapter/WgAdapter.swift b/ios/PacketTunnel/WireGuardAdapter/WgAdapter.swift
index 4bfd9b8091..eca0774e74 100644
--- a/ios/PacketTunnel/WireGuardAdapter/WgAdapter.swift
+++ b/ios/PacketTunnel/WireGuardAdapter/WgAdapter.swift
@@ -13,7 +13,7 @@ import NetworkExtension
import PacketTunnelCore
import WireGuardKit
-struct WgAdapter: TunnelAdapterProtocol {
+class WgAdapter: TunnelAdapterProtocol {
let logger = Logger(label: "WgAdapter")
let adapter: WireGuardAdapter
@@ -212,3 +212,18 @@ private extension WgStats {
return UInt64(value)
}
+
+extension WgAdapter: TunnelProvider {
+ public func tunnelHandle() throws -> Int32 {
+ return try self.adapter.tunnelHandle()
+ }
+
+ public func wgFunctions() -> WgFunctionPointers {
+ WgFunctionPointers(
+ open: adapter.inTunnelTcpOpen,
+ close: adapter.inTunnelTcpClose,
+ receive: adapter.inTunnelTcpRecv,
+ send: adapter.inTunnelTcpSend
+ )
+ }
+}
diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor+Public.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor+Public.swift
index 05b69deb35..9a9fb531c3 100644
--- a/ios/PacketTunnelCore/Actor/PacketTunnelActor+Public.swift
+++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor+Public.swift
@@ -7,6 +7,7 @@
//
import Foundation
+import MullvadTypes
import WireGuardKitTypes
/**
@@ -64,8 +65,11 @@ extension PacketTunnelActor {
- Parameter key: the new key
*/
- nonisolated public func changeEphemeralPeerNegotiationState(configuration: EphemeralPeerNegotiationState) {
- eventChannel.send(.ephemeralPeerNegotiationStateChanged(configuration))
+ nonisolated public func changeEphemeralPeerNegotiationState(
+ configuration: EphemeralPeerNegotiationState,
+ reconfigurationSemaphore: OneshotChannel
+ ) {
+ eventChannel.send(.ephemeralPeerNegotiationStateChanged(configuration, reconfigurationSemaphore))
}
/**
diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift
index 74f8a5a0e2..d9cce79f57 100644
--- a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift
+++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift
@@ -139,13 +139,14 @@ public actor PacketTunnelActor {
case let .cacheActiveKey(lastKeyRotation):
cacheActiveKey(lastKeyRotation: lastKeyRotation)
- case let .reconfigureForEphemeralPeer(configuration):
+ case let .reconfigureForEphemeralPeer(configuration, configurationSemaphore):
do {
try await updateEphemeralPeerNegotiationState(configuration: configuration)
} catch {
logger.error(error: error, message: "Failed to reconfigure tunnel after each hop negotiation.")
await setErrorStateInternal(with: error)
}
+ configurationSemaphore.send()
case .connectWithEphemeralPeer:
await connectWithEphemeralPeer()
case .setDisconnectedState:
diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift
index b677986d04..3e7ab37e54 100644
--- a/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift
+++ b/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift
@@ -7,6 +7,7 @@
//
import Foundation
+import MullvadTypes
import WireGuardKitTypes
extension PacketTunnelActor {
@@ -37,7 +38,7 @@ extension PacketTunnelActor {
case networkReachability(NetworkPath)
/// Update the device private key, as per post-quantum protocols
- case ephemeralPeerNegotiationStateChanged(EphemeralPeerNegotiationState)
+ case ephemeralPeerNegotiationStateChanged(EphemeralPeerNegotiationState, OneshotChannel)
/// Notify that an ephemeral peer exchanging took place
case notifyEphemeralPeerNegotiated
diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActorReducer.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActorReducer.swift
index 3382baf209..5b01778f2d 100644
--- a/ios/PacketTunnelCore/Actor/PacketTunnelActorReducer.swift
+++ b/ios/PacketTunnelCore/Actor/PacketTunnelActorReducer.swift
@@ -7,6 +7,7 @@
//
import Foundation
+import MullvadTypes
import WireGuardKitTypes
extension PacketTunnelActor {
@@ -25,7 +26,7 @@ extension PacketTunnelActor {
case stopTunnelAdapter
case configureForErrorState(BlockedStateReason)
case cacheActiveKey(Date?)
- case reconfigureForEphemeralPeer(EphemeralPeerNegotiationState)
+ case reconfigureForEphemeralPeer(EphemeralPeerNegotiationState, OneshotChannel)
case connectWithEphemeralPeer
// acknowledge that the disconnection process has concluded, go to .disconnected.
@@ -45,7 +46,7 @@ extension PacketTunnelActor {
case (.stopTunnelAdapter, .stopTunnelAdapter): true
case let (.configureForErrorState(r0), .configureForErrorState(r1)): r0 == r1
case let (.cacheActiveKey(d0), .cacheActiveKey(d1)): d0 == d1
- case let (.reconfigureForEphemeralPeer(eph0), .reconfigureForEphemeralPeer(eph1)): eph0 == eph1
+ case let (.reconfigureForEphemeralPeer(eph0, _), .reconfigureForEphemeralPeer(eph1, _)): eph0 == eph1
case (.connectWithEphemeralPeer, .connectWithEphemeralPeer): true
case (.setDisconnectedState, .setDisconnectedState): true
default: false
@@ -89,8 +90,8 @@ extension PacketTunnelActor {
state.mutateAssociatedData { $0.networkReachability = newReachability }
return [.updateTunnelMonitorPath(defaultPath)]
- case let .ephemeralPeerNegotiationStateChanged(configuration):
- return [.reconfigureForEphemeralPeer(configuration)]
+ case let .ephemeralPeerNegotiationStateChanged(configuration, reconfigurationSemaphore):
+ return [.reconfigureForEphemeralPeer(configuration, reconfigurationSemaphore)]
case .notifyEphemeralPeerNegotiated:
return [.connectWithEphemeralPeer]
diff --git a/ios/PacketTunnelCoreTests/EphemeralPeerExchangingPipelineTests.swift b/ios/PacketTunnelCoreTests/EphemeralPeerExchangingPipelineTests.swift
index 2af86eedfe..bbce8c8b44 100644
--- a/ios/PacketTunnelCoreTests/EphemeralPeerExchangingPipelineTests.swift
+++ b/ios/PacketTunnelCoreTests/EphemeralPeerExchangingPipelineTests.swift
@@ -60,7 +60,7 @@ final class EphemeralPeerExchangingPipelineTests: XCTestCase {
)
}
- func testSingleHopPostQuantumKeyExchange() throws {
+ func testSingleHopPostQuantumKeyExchange() async throws {
let reconfigurationExpectation = expectation(description: "Tunnel reconfiguration took place")
reconfigurationExpectation.expectedFulfillmentCount = 2
@@ -78,11 +78,11 @@ final class EphemeralPeerExchangingPipelineTests: XCTestCase {
}
keyExchangeActor.delegate = KeyExchangingResultStub(onReceivePostQuantumKey: { preSharedKey, privateKey in
- postQuantumKeyExchangingPipeline.receivePostQuantumKey(preSharedKey, ephemeralKey: privateKey)
+ await postQuantumKeyExchangingPipeline.receivePostQuantumKey(preSharedKey, ephemeralKey: privateKey)
})
let connectionState = stubConnectionState(enableMultiHop: false, enablePostQuantum: true, enableDaita: false)
- postQuantumKeyExchangingPipeline.startNegotiation(connectionState, privateKey: PrivateKey())
+ await postQuantumKeyExchangingPipeline.startNegotiation(connectionState, privateKey: PrivateKey())
wait(
for: [reconfigurationExpectation, negotiationSuccessful],
@@ -90,7 +90,7 @@ final class EphemeralPeerExchangingPipelineTests: XCTestCase {
)
}
- func testSingleHopDaitaPeerExchange() throws {
+ func testSingleHopDaitaPeerExchange() async throws {
let reconfigurationExpectation = expectation(description: "Tunnel reconfiguration took place")
reconfigurationExpectation.expectedFulfillmentCount = 2
@@ -108,11 +108,11 @@ final class EphemeralPeerExchangingPipelineTests: XCTestCase {
}
keyExchangeActor.delegate = KeyExchangingResultStub(onReceiveEphemeralPeerPrivateKey: { privateKey in
- postQuantumKeyExchangingPipeline.receiveEphemeralPeerPrivateKey(privateKey)
+ await postQuantumKeyExchangingPipeline.receiveEphemeralPeerPrivateKey(privateKey)
})
let connectionState = stubConnectionState(enableMultiHop: false, enablePostQuantum: false, enableDaita: true)
- postQuantumKeyExchangingPipeline.startNegotiation(connectionState, privateKey: PrivateKey())
+ await postQuantumKeyExchangingPipeline.startNegotiation(connectionState, privateKey: PrivateKey())
wait(
for: [reconfigurationExpectation, negotiationSuccessful],
@@ -120,7 +120,7 @@ final class EphemeralPeerExchangingPipelineTests: XCTestCase {
)
}
- func testMultiHopPostQuantumKeyExchange() throws {
+ func testMultiHopPostQuantumKeyExchange() async throws {
let reconfigurationExpectation = expectation(description: "Tunnel reconfiguration took place")
reconfigurationExpectation.expectedFulfillmentCount = 3
@@ -138,11 +138,11 @@ final class EphemeralPeerExchangingPipelineTests: XCTestCase {
}
keyExchangeActor.delegate = KeyExchangingResultStub(onReceivePostQuantumKey: { preSharedKey, privateKey in
- postQuantumKeyExchangingPipeline.receivePostQuantumKey(preSharedKey, ephemeralKey: privateKey)
+ await postQuantumKeyExchangingPipeline.receivePostQuantumKey(preSharedKey, ephemeralKey: privateKey)
})
let connectionState = stubConnectionState(enableMultiHop: true, enablePostQuantum: true, enableDaita: false)
- postQuantumKeyExchangingPipeline.startNegotiation(connectionState, privateKey: PrivateKey())
+ await postQuantumKeyExchangingPipeline.startNegotiation(connectionState, privateKey: PrivateKey())
wait(
for: [reconfigurationExpectation, negotiationSuccessful],
@@ -150,7 +150,7 @@ final class EphemeralPeerExchangingPipelineTests: XCTestCase {
)
}
- func testMultiHopDaitaExchange() throws {
+ func testMultiHopDaitaExchange() async throws {
let reconfigurationExpectation = expectation(description: "Tunnel reconfiguration took place")
reconfigurationExpectation.expectedFulfillmentCount = 3
@@ -168,11 +168,11 @@ final class EphemeralPeerExchangingPipelineTests: XCTestCase {
}
keyExchangeActor.delegate = KeyExchangingResultStub(onReceiveEphemeralPeerPrivateKey: { privateKey in
- postQuantumKeyExchangingPipeline.receiveEphemeralPeerPrivateKey(privateKey)
+ await postQuantumKeyExchangingPipeline.receiveEphemeralPeerPrivateKey(privateKey)
})
let connectionState = stubConnectionState(enableMultiHop: true, enablePostQuantum: false, enableDaita: true)
- postQuantumKeyExchangingPipeline.startNegotiation(connectionState, privateKey: PrivateKey())
+ await postQuantumKeyExchangingPipeline.startNegotiation(connectionState, privateKey: PrivateKey())
wait(
for: [reconfigurationExpectation, negotiationSuccessful],
diff --git a/ios/PacketTunnelCoreTests/Mocks/EphemeralPeerExchangeActorStub.swift b/ios/PacketTunnelCoreTests/Mocks/EphemeralPeerExchangeActorStub.swift
index 7f17af56be..2f21748820 100644
--- a/ios/PacketTunnelCoreTests/Mocks/EphemeralPeerExchangeActorStub.swift
+++ b/ios/PacketTunnelCoreTests/Mocks/EphemeralPeerExchangeActorStub.swift
@@ -22,9 +22,9 @@ final class EphemeralPeerExchangeActorStub: EphemeralPeerExchangeActorProtocol {
switch result {
case let .success((preSharedKey, ephemeralKey)):
if enablePostQuantum {
- delegate?.receivePostQuantumKey(preSharedKey, ephemeralKey: ephemeralKey)
+ Task { await delegate?.receivePostQuantumKey(preSharedKey, ephemeralKey: ephemeralKey) }
} else {
- delegate?.receiveEphemeralPeerPrivateKey(ephemeralKey)
+ Task { await delegate?.receiveEphemeralPeerPrivateKey(ephemeralKey) }
}
case .failure:
delegate?.ephemeralPeerExchangeFailed()
diff --git a/ios/PacketTunnelCoreTests/Mocks/KeyExchangingResultStub.swift b/ios/PacketTunnelCoreTests/Mocks/KeyExchangingResultStub.swift
index 9dc9dca58c..250524ec06 100644
--- a/ios/PacketTunnelCoreTests/Mocks/KeyExchangingResultStub.swift
+++ b/ios/PacketTunnelCoreTests/Mocks/KeyExchangingResultStub.swift
@@ -12,15 +12,15 @@
struct KeyExchangingResultStub: EphemeralPeerReceiving {
var onFailure: (() -> Void)?
- var onReceivePostQuantumKey: ((PreSharedKey, PrivateKey) -> Void)?
- var onReceiveEphemeralPeerPrivateKey: ((PrivateKey) -> Void)?
+ var onReceivePostQuantumKey: ((PreSharedKey, PrivateKey) async -> Void)?
+ var onReceiveEphemeralPeerPrivateKey: ((PrivateKey) async -> Void)?
- func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) {
- onReceivePostQuantumKey?(key, ephemeralKey)
+ func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) async {
+ await onReceivePostQuantumKey?(key, ephemeralKey)
}
- public func receiveEphemeralPeerPrivateKey(_ ephemeralPeerPrivateKey: PrivateKey) {
- onReceiveEphemeralPeerPrivateKey?(ephemeralPeerPrivateKey)
+ public func receiveEphemeralPeerPrivateKey(_ ephemeralPeerPrivateKey: PrivateKey) async {
+ await onReceiveEphemeralPeerPrivateKey?(ephemeralPeerPrivateKey)
}
func ephemeralPeerExchangeFailed() {
diff --git a/ios/PacketTunnelCoreTests/MultiHopEphemeralPeerExchangerTests.swift b/ios/PacketTunnelCoreTests/MultiHopEphemeralPeerExchangerTests.swift
index a4c1d09155..c55f5b4d65 100644
--- a/ios/PacketTunnelCoreTests/MultiHopEphemeralPeerExchangerTests.swift
+++ b/ios/PacketTunnelCoreTests/MultiHopEphemeralPeerExchangerTests.swift
@@ -59,7 +59,7 @@ final class MultiHopEphemeralPeerExchangerTests: XCTestCase {
)
}
- func testEphemeralPeerExchangeFailsWhenNegotiationCannotStart() {
+ func testEphemeralPeerExchangeFailsWhenNegotiationCannotStart() async {
let expectedNegotiationFailure = expectation(description: "Negotiation failed.")
let reconfigurationExpectation = expectation(description: "Tunnel reconfiguration took place")
@@ -88,7 +88,7 @@ final class MultiHopEphemeralPeerExchangerTests: XCTestCase {
expectedNegotiationFailure.fulfill()
}
- multiHopExchanger.start()
+ await multiHopExchanger.start()
wait(
for: [expectedNegotiationFailure, reconfigurationExpectation, negotiationSuccessful],
@@ -96,7 +96,7 @@ final class MultiHopEphemeralPeerExchangerTests: XCTestCase {
)
}
- func testEphemeralPeerExchangeSuccessWhenPostQuantumNegotiationStarts() throws {
+ func testEphemeralPeerExchangeSuccessWhenPostQuantumNegotiationStarts() async throws {
let unexpectedNegotiationFailure = expectation(description: "Negotiation failed.")
unexpectedNegotiationFailure.isInverted = true
@@ -124,9 +124,9 @@ final class MultiHopEphemeralPeerExchangerTests: XCTestCase {
}
peerExchangeActor.delegate = KeyExchangingResultStub(onReceivePostQuantumKey: { preSharedKey, ephemeralKey in
- multiHopPeerExchanger.receivePostQuantumKey(preSharedKey, ephemeralKey: ephemeralKey)
+ await multiHopPeerExchanger.receivePostQuantumKey(preSharedKey, ephemeralKey: ephemeralKey)
})
- multiHopPeerExchanger.start()
+ await multiHopPeerExchanger.start()
wait(
for: [unexpectedNegotiationFailure, reconfigurationExpectation, negotiationSuccessful],
@@ -134,7 +134,7 @@ final class MultiHopEphemeralPeerExchangerTests: XCTestCase {
)
}
- func testEphemeralPeerExchangeSuccessWhenDaitaNegotiationStarts() throws {
+ func testEphemeralPeerExchangeSuccessWhenDaitaNegotiationStarts() async throws {
let unexpectedNegotiationFailure = expectation(description: "Negotiation failed.")
unexpectedNegotiationFailure.isInverted = true
@@ -162,9 +162,9 @@ final class MultiHopEphemeralPeerExchangerTests: XCTestCase {
}
peerExchangeActor.delegate = KeyExchangingResultStub(onReceiveEphemeralPeerPrivateKey: { ephemeralKey in
- multiHopPeerExchanger.receiveEphemeralPeerPrivateKey(ephemeralKey)
+ await multiHopPeerExchanger.receiveEphemeralPeerPrivateKey(ephemeralKey)
})
- multiHopPeerExchanger.start()
+ await multiHopPeerExchanger.start()
wait(
for: [unexpectedNegotiationFailure, reconfigurationExpectation, negotiationSuccessful],
diff --git a/ios/PacketTunnelCoreTests/SingleHopEphemeralPeerExchangerTests.swift b/ios/PacketTunnelCoreTests/SingleHopEphemeralPeerExchangerTests.swift
index 2ce3558fba..deb402abeb 100644
--- a/ios/PacketTunnelCoreTests/SingleHopEphemeralPeerExchangerTests.swift
+++ b/ios/PacketTunnelCoreTests/SingleHopEphemeralPeerExchangerTests.swift
@@ -38,7 +38,7 @@ final class SingleHopEphemeralPeerExchangerTests: XCTestCase {
exitRelay = SelectedRelay(endpoint: match.endpoint, hostname: match.relay.hostname, location: match.location)
}
- func testEphemeralPeerExchangeFailsWhenNegotiationCannotStart() {
+ func testEphemeralPeerExchangeFailsWhenNegotiationCannotStart() async {
let expectedNegotiationFailure = expectation(description: "Negotiation failed.")
let reconfigurationExpectation = expectation(description: "Tunnel reconfiguration took place")
@@ -66,7 +66,7 @@ final class SingleHopEphemeralPeerExchangerTests: XCTestCase {
expectedNegotiationFailure.fulfill()
}
- singleHopPostQuantumKeyExchanging.start()
+ await singleHopPostQuantumKeyExchanging.start()
wait(
for: [expectedNegotiationFailure, reconfigurationExpectation, negotiationSuccessful],
@@ -74,7 +74,7 @@ final class SingleHopEphemeralPeerExchangerTests: XCTestCase {
)
}
- func testEphemeralPeerExchangeSuccessWhenPostQuantumNegotiationStarts() throws {
+ func testEphemeralPeerExchangeSuccessWhenPostQuantumNegotiationStarts() async throws {
let unexpectedNegotiationFailure = expectation(description: "Negotiation failed.")
unexpectedNegotiationFailure.isInverted = true
@@ -101,9 +101,9 @@ final class SingleHopEphemeralPeerExchangerTests: XCTestCase {
}
keyExchangeActor.delegate = KeyExchangingResultStub(onReceivePostQuantumKey: { preSharedKey, ephemeralKey in
- singleHopPostQuantumKeyExchanging.receivePostQuantumKey(preSharedKey, ephemeralKey: ephemeralKey)
+ await singleHopPostQuantumKeyExchanging.receivePostQuantumKey(preSharedKey, ephemeralKey: ephemeralKey)
})
- singleHopPostQuantumKeyExchanging.start()
+ await singleHopPostQuantumKeyExchanging.start()
wait(
for: [unexpectedNegotiationFailure, reconfigurationExpectation, negotiationSuccessful],
@@ -111,7 +111,7 @@ final class SingleHopEphemeralPeerExchangerTests: XCTestCase {
)
}
- func testEphemeralPeerExchangeSuccessWhenDaitaNegotiationStarts() throws {
+ func testEphemeralPeerExchangeSuccessWhenDaitaNegotiationStarts() async throws {
let unexpectedNegotiationFailure = expectation(description: "Negotiation failed.")
unexpectedNegotiationFailure.isInverted = true
@@ -138,9 +138,9 @@ final class SingleHopEphemeralPeerExchangerTests: XCTestCase {
}
peerExchangeActor.delegate = KeyExchangingResultStub(onReceiveEphemeralPeerPrivateKey: { ephemeralKey in
- multiHopPeerExchanger.receiveEphemeralPeerPrivateKey(ephemeralKey)
+ await multiHopPeerExchanger.receiveEphemeralPeerPrivateKey(ephemeralKey)
})
- multiHopPeerExchanger.start()
+ await multiHopPeerExchanger.start()
wait(
for: [unexpectedNegotiationFailure, reconfigurationExpectation, negotiationSuccessful],