summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBug Magnet <marco.nikic@mullvad.net>2024-06-17 13:22:40 +0200
committerBug Magnet <marco.nikic@mullvad.net>2024-06-17 13:22:40 +0200
commit4204b0facd9e9eab031b006e613c225082af73e9 (patch)
treee34950bd106122c0566f0220a672703b2fa0c5d7
parent4b4c9cc89f97dc3cb5fc93305250b2a8d2b42cb3 (diff)
parent0af08f5096693881a03c5914906e783b7dd99288 (diff)
downloadmullvadvpn-4204b0facd9e9eab031b006e613c225082af73e9.tar.xz
mullvadvpn-4204b0facd9e9eab031b006e613c225082af73e9.zip
Merge branch 'add-timeout-to-the-tcp-connection-for-the-pq-key-negotiation-ios-701'
-rw-r--r--ios/MullvadPostQuantum/PostQuantumKeyNegotiator.swift35
-rw-r--r--ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h24
-rw-r--r--ios/MullvadPostQuantumTests/MullvadPostQuantum+Stubs.swift95
-rw-r--r--ios/MullvadPostQuantumTests/PostQuantumKeyExchangeActorTests.swift91
-rw-r--r--ios/MullvadREST/RetryStrategy/RetryStrategy.swift10
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj241
-rw-r--r--ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadPostQuantumTests.xcscheme54
-rw-r--r--ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme14
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift7
-rw-r--r--ios/PacketTunnel/PostQuantumKeyExchangeActor.swift52
-rw-r--r--ios/PacketTunnelCore/NEPacketTunnelProvider+Extensions.swift21
-rw-r--r--ios/TestPlans/MullvadVPNApp.xctestplan7
-rw-r--r--ios/TestPlans/MullvadVPNCI.xctestplan7
-rw-r--r--talpid-tunnel-config-client/src/ios_ffi/ios_runtime.rs63
-rw-r--r--talpid-tunnel-config-client/src/ios_ffi/ios_tcp_connection.rs101
-rw-r--r--talpid-tunnel-config-client/src/ios_ffi/mod.rs47
16 files changed, 747 insertions, 122 deletions
diff --git a/ios/MullvadPostQuantum/PostQuantumKeyNegotiator.swift b/ios/MullvadPostQuantum/PostQuantumKeyNegotiator.swift
index 16f7a24931..6876686fd0 100644
--- a/ios/MullvadPostQuantum/PostQuantumKeyNegotiator.swift
+++ b/ios/MullvadPostQuantum/PostQuantumKeyNegotiator.swift
@@ -7,15 +7,33 @@
//
import Foundation
+import MullvadTypes
import NetworkExtension
+import PacketTunnelCore
import TalpidTunnelConfigClientProxy
import WireGuardKitTypes
+// swiftlint:disable function_parameter_count
+public protocol PostQuantumKeyNegotiating {
+ func startNegotiation(
+ gatewayIP: IPv4Address,
+ devicePublicKey: PublicKey,
+ presharedKey: PrivateKey,
+ packetTunnel: any TunnelProvider,
+ tcpConnection: NWTCPConnection,
+ postQuantumKeyExchangeTimeout: Duration
+ ) -> Bool
+
+ func cancelKeyNegotiation()
+
+ init()
+}
+
/**
Attempt to start the asynchronous process of key negotiation. Returns true if successfully started, false if failed.
*/
-public class PostQuantumKeyNegotiator {
- public init() {}
+public class PostQuantumKeyNegotiator: PostQuantumKeyNegotiating {
+ required public init() {}
var cancelToken: PostQuantumCancelToken?
@@ -23,10 +41,12 @@ public class PostQuantumKeyNegotiator {
gatewayIP: IPv4Address,
devicePublicKey: PublicKey,
presharedKey: PrivateKey,
- packetTunnel: NEPacketTunnelProvider,
- tcpConnection: NWTCPConnection
+ packetTunnel: any TunnelProvider,
+ tcpConnection: NWTCPConnection,
+ postQuantumKeyExchangeTimeout: Duration
) -> Bool {
- let packetTunnelPointer = Unmanaged.passUnretained(packetTunnel).toOpaque()
+ // swiftlint:disable:next force_cast
+ let packetTunnelPointer = Unmanaged.passUnretained(packetTunnel as! NEPacketTunnelProvider).toOpaque()
let opaqueConnection = Unmanaged.passUnretained(tcpConnection).toOpaque()
var cancelToken = PostQuantumCancelToken()
@@ -35,7 +55,8 @@ public class PostQuantumKeyNegotiator {
presharedKey.rawValue.map { $0 },
packetTunnelPointer,
opaqueConnection,
- &cancelToken
+ &cancelToken,
+ UInt64(postQuantumKeyExchangeTimeout.timeInterval)
)
guard result == 0 else {
return false
@@ -54,3 +75,5 @@ public class PostQuantumKeyNegotiator {
drop_post_quantum_key_exchange_token(&cancelToken)
}
}
+
+// swiftlint:enable function_parameter_count
diff --git a/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h b/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h
index 04180db289..31d31748ae 100644
--- a/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h
+++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h
@@ -16,15 +16,18 @@ typedef struct PostQuantumCancelToken {
* Called by the Swift side to signal that the quantum-secure key exchange should be cancelled.
*
* # Safety
- * `sender` must be pointing to a valid instance of a `PostQuantumCancelToken` created by the `PacketTunnelProvider`.
+ * `sender` must be pointing to a valid instance of a `PostQuantumCancelToken` created by the
+ * `PacketTunnelProvider`.
*/
void cancel_post_quantum_key_exchange(const struct PostQuantumCancelToken *sender);
/**
- * Called by the Swift side to signal that the Rust `PostQuantumCancelToken` can be safely dropped from memory.
+ * Called by the Swift side to signal that the Rust `PostQuantumCancelToken` can be safely dropped
+ * from memory.
*
* # Safety
- * `sender` must be pointing to a valid instance of a `PostQuantumCancelToken` created by the `PacketTunnelProvider`.
+ * `sender` must be pointing to a valid instance of a `PostQuantumCancelToken` created by the
+ * `PacketTunnelProvider`.
*/
void drop_post_quantum_key_exchange_token(const struct PostQuantumCancelToken *sender);
@@ -44,17 +47,15 @@ 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.
*
- * 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.
+ * 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 handle_recv(const uint8_t *data, uintptr_t data_len, const void *sender);
/**
* Entry point for exchanging post quantum keys on iOS.
@@ -62,15 +63,16 @@ void handle_recv(const uint8_t *data,
* # 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.
+ * `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 negotiate_post_quantum_key(const uint8_t *public_key,
const uint8_t *ephemeral_key,
const void *packet_tunnel,
const void *tcp_connection,
- struct PostQuantumCancelToken *cancel_token);
+ struct PostQuantumCancelToken *cancel_token,
+ uint64_t post_quantum_key_exchange_timeout);
/**
* Called when there is data to send on the TCP connection.
diff --git a/ios/MullvadPostQuantumTests/MullvadPostQuantum+Stubs.swift b/ios/MullvadPostQuantumTests/MullvadPostQuantum+Stubs.swift
new file mode 100644
index 0000000000..1df6584444
--- /dev/null
+++ b/ios/MullvadPostQuantumTests/MullvadPostQuantum+Stubs.swift
@@ -0,0 +1,95 @@
+//
+// MullvadPostQuantum+Stubs.swift
+// MullvadPostQuantumTests
+//
+// Created by Marco Nikic on 2024-06-12.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+@testable import MullvadPostQuantum
+@testable import MullvadTypes
+import NetworkExtension
+@testable import PacketTunnelCore
+@testable import WireGuardKitTypes
+
+// swiftlint:disable function_parameter_count
+class NWTCPConnectionStub: NWTCPConnection {
+ var _isViable = false
+ override var isViable: Bool {
+ _isViable
+ }
+
+ func becomeViable() {
+ willChangeValue(for: \.isViable)
+ _isViable = true
+ didChangeValue(for: \.isViable)
+ }
+}
+
+class TunnelProviderStub: TunnelProvider {
+ let tcpConnection: NWTCPConnectionStub
+
+ init(tcpConnection: NWTCPConnectionStub) {
+ self.tcpConnection = tcpConnection
+ }
+
+ func createTCPConnectionThroughTunnel(
+ to remoteEndpoint: NWEndpoint,
+ enableTLS: Bool,
+ tlsParameters TLSParameters: NWTLSParameters?,
+ delegate: Any?
+ ) -> NWTCPConnection {
+ tcpConnection
+ }
+}
+
+class FailedNegotiatorStub: PostQuantumKeyNegotiating {
+ var onCancelKeyNegotiation: (() -> Void)?
+
+ required init() {
+ onCancelKeyNegotiation = nil
+ }
+
+ init(onCancelKeyNegotiation: (() -> Void)? = nil) {
+ self.onCancelKeyNegotiation = onCancelKeyNegotiation
+ }
+
+ func startNegotiation(
+ gatewayIP: IPv4Address,
+ devicePublicKey: WireGuardKitTypes.PublicKey,
+ presharedKey: WireGuardKitTypes.PrivateKey,
+ packetTunnel: PacketTunnelCore.TunnelProvider,
+ tcpConnection: NWTCPConnection,
+ postQuantumKeyExchangeTimeout: MullvadTypes.Duration
+ ) -> Bool { false }
+
+ func cancelKeyNegotiation() {
+ onCancelKeyNegotiation?()
+ }
+}
+
+class SuccessfulNegotiatorStub: PostQuantumKeyNegotiating {
+ var onCancelKeyNegotiation: (() -> Void)?
+ required init() {
+ onCancelKeyNegotiation = nil
+ }
+
+ init(onCancelKeyNegotiation: (() -> Void)? = nil) {
+ self.onCancelKeyNegotiation = onCancelKeyNegotiation
+ }
+
+ func startNegotiation(
+ gatewayIP: IPv4Address,
+ devicePublicKey: WireGuardKitTypes.PublicKey,
+ presharedKey: WireGuardKitTypes.PrivateKey,
+ packetTunnel: PacketTunnelCore.TunnelProvider,
+ tcpConnection: NWTCPConnection,
+ postQuantumKeyExchangeTimeout: MullvadTypes.Duration
+ ) -> Bool { true }
+
+ func cancelKeyNegotiation() {
+ onCancelKeyNegotiation?()
+ }
+}
+
+// swiftlint:enable function_parameter_count
diff --git a/ios/MullvadPostQuantumTests/PostQuantumKeyExchangeActorTests.swift b/ios/MullvadPostQuantumTests/PostQuantumKeyExchangeActorTests.swift
new file mode 100644
index 0000000000..ca485a0c19
--- /dev/null
+++ b/ios/MullvadPostQuantumTests/PostQuantumKeyExchangeActorTests.swift
@@ -0,0 +1,91 @@
+//
+// PostQuantumKeyExchangeActorTests.swift
+// PostQuantumKeyExchangeActorTests
+//
+// Created by Marco Nikic on 2024-06-12.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+@testable import MullvadPostQuantum
+@testable import MullvadTypes
+import NetworkExtension
+@testable import PacketTunnelCore
+@testable import WireGuardKitTypes
+import XCTest
+
+class MullvadPostQuantumTests: XCTestCase {
+ var tcpConnection: NWTCPConnectionStub!
+ var tunnelProvider: TunnelProviderStub!
+
+ override func setUpWithError() throws {
+ tcpConnection = NWTCPConnectionStub()
+ tunnelProvider = TunnelProviderStub(tcpConnection: tcpConnection)
+ }
+
+ func testKeyExchangeFailsWhenNegotiationCannotStart() {
+ let negotiationFailure = expectation(description: "Negotiation failed")
+
+ let keyExchangeActor = PostQuantumKeyExchangeActor(
+ packetTunnel: tunnelProvider,
+ onFailure: {
+ negotiationFailure.fulfill()
+ },
+ negotiationProvider: FailedNegotiatorStub.self
+ )
+
+ let privateKey = PrivateKey()
+ keyExchangeActor.startNegotiation(with: privateKey)
+ tcpConnection.becomeViable()
+
+ wait(for: [negotiationFailure])
+ }
+
+ func testKeyExchangeFailsWhenTCPConnectionIsNotReadyInTime() {
+ let negotiationFailure = expectation(description: "Negotiation failed")
+
+ // Setup the actor to wait only 10 milliseconds before declaring the TCP connection is not ready in time.
+ let keyExchangeActor = PostQuantumKeyExchangeActor(
+ packetTunnel: tunnelProvider,
+ onFailure: {
+ negotiationFailure.fulfill()
+ },
+ negotiationProvider: FailedNegotiatorStub.self,
+ keyExchangeRetriesIterator: AnyIterator { .milliseconds(10) }
+ )
+
+ let privateKey = PrivateKey()
+ keyExchangeActor.startNegotiation(with: privateKey)
+
+ wait(for: [negotiationFailure])
+ }
+
+ func testResetEndsTheCurrentNegotiation() throws {
+ let unexpectedNegotiationFailure = expectation(description: "Unexpected negotiation failure")
+ unexpectedNegotiationFailure.isInverted = true
+
+ let keyExchangeActor = PostQuantumKeyExchangeActor(
+ packetTunnel: tunnelProvider,
+ onFailure: {
+ unexpectedNegotiationFailure.fulfill()
+ },
+ negotiationProvider: SuccessfulNegotiatorStub.self
+ )
+
+ let privateKey = PrivateKey()
+ keyExchangeActor.startNegotiation(with: privateKey)
+
+ let negotiationProvider = try XCTUnwrap(
+ keyExchangeActor.negotiation?
+ .negotiator as? SuccessfulNegotiatorStub
+ )
+
+ let negotationCancelledExpectation = expectation(description: "Negotiation cancelled")
+ negotiationProvider.onCancelKeyNegotiation = {
+ negotationCancelledExpectation.fulfill()
+ }
+
+ keyExchangeActor.reset()
+
+ wait(for: [negotationCancelledExpectation, unexpectedNegotiationFailure], timeout: 0.5)
+ }
+}
diff --git a/ios/MullvadREST/RetryStrategy/RetryStrategy.swift b/ios/MullvadREST/RetryStrategy/RetryStrategy.swift
index 18e3cd69f3..82e31abd2b 100644
--- a/ios/MullvadREST/RetryStrategy/RetryStrategy.swift
+++ b/ios/MullvadREST/RetryStrategy/RetryStrategy.swift
@@ -68,6 +68,16 @@ extension REST {
multiplier: 2,
maxDelay: .seconds(8)
)
+
+ public static var postQuantumKeyExchange = RetryStrategy(
+ maxRetryCount: 10,
+ delay: .exponentialBackoff(
+ initial: .seconds(10),
+ multiplier: UInt64(2),
+ maxDelay: .seconds(30)
+ ),
+ applyJitter: true
+ )
}
public enum RetryDelay: Equatable {
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 60b89d512d..faa02954e7 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -699,9 +699,10 @@
A944F2632B8DEFDB00473F4C /* MullvadPostQuantum.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
A944F26A2B8DF32900473F4C /* PostQuantumKeyNegotiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB4F9C2B7FAB21002A2D7A /* PostQuantumKeyNegotiator.swift */; };
A944F2722B8E02F600473F4C /* libtalpid_tunnel_config_client.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A944F2712B8E02E800473F4C /* libtalpid_tunnel_config_client.a */; };
- A948809B2BC9308D0090A44C /* PostQuantumKeyExchangeActor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A948809A2BC9308D0090A44C /* PostQuantumKeyExchangeActor.swift */; };
A94D691A2ABAD66700413DD4 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 58FE25E22AA72AE9003D1918 /* WireGuardKitTypes */; };
A94D691B2ABAD66700413DD4 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 58FE25E72AA7399D003D1918 /* WireGuardKitTypes */; };
+ A959FC4E2C19CF70009733CD /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = A959FC4D2C19CF70009733CD /* WireGuardKitTypes */; };
+ A959FC502C19CF70009733CD /* WireGuardKitTypes in Embed Frameworks */ = {isa = PBXBuildFile; productRef = A959FC4D2C19CF70009733CD /* WireGuardKitTypes */; };
A95EEE362B722CD600A8A39B /* TunnelMonitorState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95EEE352B722CD600A8A39B /* TunnelMonitorState.swift */; };
A95EEE382B722DFC00A8A39B /* PingStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95EEE372B722DFC00A8A39B /* PingStats.swift */; };
A9630E3C2B8E0E7B00A65999 /* talpid_tunnel_config_client.h in Headers */ = {isa = PBXBuildFile; fileRef = A9630E3B2B8E0E7B00A65999 /* talpid_tunnel_config_client.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -719,6 +720,10 @@
A988A3E22AFE54AC0008D2C7 /* AccountExpiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FA62AFBB9AE006D0856 /* AccountExpiry.swift */; };
A988DF272ADE86ED00D807EF /* WireGuardObfuscationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A988DF252ADE86ED00D807EF /* WireGuardObfuscationSettings.swift */; };
A988DF2A2ADE880300D807EF /* TunnelSettingsV3.swift in Sources */ = {isa = PBXBuildFile; fileRef = A988DF282ADE880300D807EF /* TunnelSettingsV3.swift */; };
+ A98F1B452C19BB83003C869E /* NEPacketTunnelProvider+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98F1B442C19BB83003C869E /* NEPacketTunnelProvider+Extensions.swift */; };
+ A98F1B512C19C48D003C869E /* PostQuantumKeyExchangeActorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98F1B502C19C48D003C869E /* PostQuantumKeyExchangeActorTests.swift */; };
+ A98F1B522C19C48D003C869E /* MullvadPostQuantum.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */; platformFilter = ios; };
+ A98F1B5A2C19C4C4003C869E /* PostQuantumKeyExchangeActor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A948809A2BC9308D0090A44C /* PostQuantumKeyExchangeActor.swift */; };
A998DA812BD147AD001D61A2 /* ListCustomListsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A998DA802BD147AD001D61A2 /* ListCustomListsPage.swift */; };
A998DA832BD2B055001D61A2 /* EditCustomListLocationsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A998DA822BD2B055001D61A2 /* EditCustomListLocationsPage.swift */; };
A99E5EE02B7628150033F241 /* ProblemReportViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A99E5EDF2B7628150033F241 /* ProblemReportViewModel.swift */; };
@@ -827,6 +832,7 @@
A9BA08322BA32FB6005A7A2D /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = A92962582B1F4FDB00DFB93B /* PrivacyInfo.xcprivacy */; };
A9BFAFFF2BD004ED00F2BCA1 /* CustomListsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BFAFFE2BD004ED00F2BCA1 /* CustomListsTests.swift */; };
A9BFB0012BD00B7F00F2BCA1 /* CustomListPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BFB0002BD00B7F00F2BCA1 /* CustomListPage.swift */; };
+ A9C3083A2C19DDA7008715F1 /* MullvadPostQuantum+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C308392C19DDA7008715F1 /* MullvadPostQuantum+Stubs.swift */; };
A9C342C12ACC37E30045F00E /* TunnelStatusBlockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E0317D2ACC32920095D843 /* TunnelStatusBlockObserver.swift */; };
A9C342C32ACC3EE90045F00E /* RelayCacheTracker+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C342C22ACC3EE90045F00E /* RelayCacheTracker+Stubs.swift */; };
A9D7E43C2BFCE43200213D55 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = A906F9492BA1E09A002BF22E /* WireGuardKitTypes */; };
@@ -1232,6 +1238,20 @@
remoteGlobalIDString = 58D223D4294C8E5E0029F5F8;
remoteInfo = MullvadTypes;
};
+ A98F1B462C19BCFF003C869E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 58CE5E58224146200008646E /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 58C7A4352A863F440060C66F;
+ remoteInfo = PacketTunnelCore;
+ };
+ A98F1B532C19C48D003C869E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 58CE5E58224146200008646E /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = A944F25B2B8DEFDB00473F4C;
+ remoteInfo = MullvadPostQuantum;
+ };
A9EC20F12A5D79ED0040D56E /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 58CE5E58224146200008646E /* Project object */;
@@ -1350,6 +1370,17 @@
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
+ A959FC4F2C19CF70009733CD /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ A959FC502C19CF70009733CD /* WireGuardKitTypes in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F0ACE3292BE4E712006D5333 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@@ -2063,6 +2094,9 @@
A98502022B627B120061901E /* LocalNetworkProbe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNetworkProbe.swift; sourceTree = "<group>"; };
A988DF252ADE86ED00D807EF /* WireGuardObfuscationSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireGuardObfuscationSettings.swift; sourceTree = "<group>"; };
A988DF282ADE880300D807EF /* TunnelSettingsV3.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelSettingsV3.swift; sourceTree = "<group>"; };
+ A98F1B442C19BB83003C869E /* NEPacketTunnelProvider+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NEPacketTunnelProvider+Extensions.swift"; sourceTree = "<group>"; };
+ A98F1B4E2C19C48D003C869E /* MullvadPostQuantumTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MullvadPostQuantumTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ A98F1B502C19C48D003C869E /* PostQuantumKeyExchangeActorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostQuantumKeyExchangeActorTests.swift; sourceTree = "<group>"; };
A998DA802BD147AD001D61A2 /* ListCustomListsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCustomListsPage.swift; sourceTree = "<group>"; };
A998DA822BD2B055001D61A2 /* EditCustomListLocationsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditCustomListLocationsPage.swift; sourceTree = "<group>"; };
A99E5EDF2B7628150033F241 /* ProblemReportViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProblemReportViewModel.swift; sourceTree = "<group>"; };
@@ -2075,6 +2109,7 @@
A9B6AC192ADE8FBB00F7802A /* InMemorySettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InMemorySettingsStore.swift; sourceTree = "<group>"; };
A9BFAFFE2BD004ED00F2BCA1 /* CustomListsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomListsTests.swift; sourceTree = "<group>"; };
A9BFB0002BD00B7F00F2BCA1 /* CustomListPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomListPage.swift; sourceTree = "<group>"; };
+ A9C308392C19DDA7008715F1 /* MullvadPostQuantum+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MullvadPostQuantum+Stubs.swift"; sourceTree = "<group>"; };
A9C342C22ACC3EE90045F00E /* RelayCacheTracker+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RelayCacheTracker+Stubs.swift"; sourceTree = "<group>"; };
A9CF11FC2A0518E7001D9565 /* AddressCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressCacheTests.swift; sourceTree = "<group>"; };
A9D96B192A8247C100A5C673 /* MigrationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationManager.swift; sourceTree = "<group>"; };
@@ -2357,6 +2392,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ A98F1B4B2C19C48D003C869E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ A959FC4E2C19CF70009733CD /* WireGuardKitTypes in Frameworks */,
+ A98F1B522C19C48D003C869E /* MullvadPostQuantum.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F0ACE3052BE4E478006D5333 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -3383,6 +3427,7 @@
5864AF802A9F52E3008BC928 /* Actor */,
58C7A42E2A85091B0060C66F /* Pinger */,
58E072A228814B96008902F8 /* TunnelMonitor */,
+ A98F1B442C19BB83003C869E /* NEPacketTunnelProvider+Extensions.swift */,
);
path = PacketTunnelCore;
sourceTree = "<group>";
@@ -3482,6 +3527,7 @@
584023202A406BF5007B27AC /* TunnelObfuscation */,
58695A9E2A4ADA9200328DB3 /* TunnelObfuscationTests */,
A944F25D2B8DEFDB00473F4C /* MullvadPostQuantum */,
+ A98F1B4F2C19C48D003C869E /* MullvadPostQuantumTests */,
58CE5E61224146200008646E /* Products */,
584F991F2902CBDD001F858D /* Frameworks */,
);
@@ -3509,6 +3555,7 @@
852969252B4D9C1F007EAD4C /* MullvadVPNUITests.xctest */,
F0ACE3082BE4E478006D5333 /* MullvadMockData.framework */,
A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */,
+ A98F1B4E2C19C48D003C869E /* MullvadPostQuantumTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -3913,7 +3960,6 @@
852969262B4D9C1F007EAD4C /* MullvadVPNUITests */ = {
isa = PBXGroup;
children = (
- 8518F6392B601910009EB113 /* Base */,
85557B0C2B591B0F00795FE1 /* Networking */,
852969312B4E9220007EAD4C /* Pages */,
7A45CFCD2C08697100D80B21 /* Screenshots */,
@@ -3926,7 +3972,7 @@
850201DA2B503D7700EF8C96 /* RelayTests.swift */,
85D039972BA4711800940E7F /* SettingsMigrationTests.swift */,
85C7A2E82B89024B00035D5A /* SettingsTests.swift */,
- 8518F6392B601910009EB113 /* Test base classes */,
+ 8518F6392B601910009EB113 /* Base */,
856952E12BD6B04C008C1F84 /* XCUIElement+Extensions.swift */,
85557B152B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift */,
);
@@ -3938,15 +3984,12 @@
children = (
85FB5A0F2B6960A30015DCED /* AccountDeletionPage.swift */,
85557B1F2B5FBBD700795FE1 /* AccountPage.swift */,
- 852D054E2BC43DF7008578D2 /* AddAccessMethodPage.swift */,
7ACD79382C0DAADC00DBEE14 /* AddCustomListLocationsPage.swift */,
8529693B2B4F0257007EAD4C /* Alert.swift */,
- 852D054C2BC3DE3A008578D2 /* APIAccessPage.swift */,
8587A05C2B84D43100152938 /* ChangeLogAlert.swift */,
A9BFB0002BD00B7F00F2BCA1 /* CustomListPage.swift */,
85A42B872BB44D31007BABF7 /* DeviceManagementPage.swift */,
852A26452BA9C9CB006EB9C8 /* DNSSettingsPage.swift */,
- 8585CBE22BC684180015B6A4 /* EditAccessMethodPage.swift */,
A998DA822BD2B055001D61A2 /* EditCustomListLocationsPage.swift */,
85557B1D2B5FB8C700795FE1 /* HeaderBar.swift */,
A998DA802BD147AD001D61A2 /* ListCustomListsPage.swift */,
@@ -3957,7 +4000,6 @@
855D9F5A2B63E56B00D7C64D /* ProblemReportPage.swift */,
8532E6862B8CCED600ACECD1 /* ProblemReportSubmittedPage.swift */,
8556EB552B9B0AC500D26DD4 /* RevokedDevicePage.swift */,
- 8542F7522BCFBD050035C042 /* SelectLocationFilterPage.swift */,
850201DC2B503D8C00EF8C96 /* SelectLocationPage.swift */,
850201E22B51A93C00EF8C96 /* SettingsPage.swift */,
852969392B4F0238007EAD4C /* TermsOfServicePage.swift */,
@@ -4024,6 +4066,15 @@
path = MullvadPostQuantum;
sourceTree = "<group>";
};
+ A98F1B4F2C19C48D003C869E /* MullvadPostQuantumTests */ = {
+ isa = PBXGroup;
+ children = (
+ A98F1B502C19C48D003C869E /* PostQuantumKeyExchangeActorTests.swift */,
+ A9C308392C19DDA7008715F1 /* MullvadPostQuantum+Stubs.swift */,
+ );
+ path = MullvadPostQuantumTests;
+ sourceTree = "<group>";
+ };
F028A5472A336E1900C0CAA3 /* RedeemVoucher */ = {
isa = PBXGroup;
children = (
@@ -4746,6 +4797,7 @@
buildRules = (
);
dependencies = (
+ A98F1B472C19BCFF003C869E /* PBXTargetDependency */,
A9630E422B8E10F700A65999 /* PBXTargetDependency */,
);
name = MullvadPostQuantum;
@@ -4756,6 +4808,28 @@
productReference = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */;
productType = "com.apple.product-type.framework";
};
+ A98F1B4D2C19C48D003C869E /* MullvadPostQuantumTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = A98F1B552C19C48D003C869E /* Build configuration list for PBXNativeTarget "MullvadPostQuantumTests" */;
+ buildPhases = (
+ A98F1B4A2C19C48D003C869E /* Sources */,
+ A98F1B4B2C19C48D003C869E /* Frameworks */,
+ A98F1B4C2C19C48D003C869E /* Resources */,
+ A959FC4F2C19CF70009733CD /* Embed Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ A98F1B542C19C48D003C869E /* PBXTargetDependency */,
+ );
+ name = MullvadPostQuantumTests;
+ packageProductDependencies = (
+ A959FC4D2C19CF70009733CD /* WireGuardKitTypes */,
+ );
+ productName = MullvadPostQuantumTests;
+ productReference = A98F1B4E2C19C48D003C869E /* MullvadPostQuantumTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
F0ACE3072BE4E478006D5333 /* MullvadMockData */ = {
isa = PBXNativeTarget;
buildConfigurationList = F0ACE3162BE4E479006D5333 /* Build configuration list for PBXNativeTarget "MullvadMockData" */;
@@ -4785,7 +4859,7 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
- LastSwiftUpdateCheck = 1510;
+ LastSwiftUpdateCheck = 1520;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "Mullvad VPN AB";
TargetAttributes = {
@@ -4863,6 +4937,9 @@
A944F25B2B8DEFDB00473F4C = {
CreatedOnToolsVersion = 15.2;
};
+ A98F1B4D2C19C48D003C869E = {
+ CreatedOnToolsVersion = 15.2;
+ };
F0ACE3072BE4E478006D5333 = {
CreatedOnToolsVersion = 15.2;
};
@@ -4905,6 +4982,7 @@
7A88DCD62A8FABBE00D2FF0E /* RoutingTests */,
F0ACE3072BE4E478006D5333 /* MullvadMockData */,
A944F25B2B8DEFDB00473F4C /* MullvadPostQuantum */,
+ A98F1B4D2C19C48D003C869E /* MullvadPostQuantumTests */,
);
};
/* End PBXProject section */
@@ -5036,6 +5114,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ A98F1B4C2C19C48D003C869E /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F0ACE3062BE4E478006D5333 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -5528,6 +5613,7 @@
586C145A2AC4735F00245C01 /* PacketTunnelActor+Public.swift in Sources */,
F0DAC8AD2C16EFE400F80144 /* TunnelSettingsManager.swift in Sources */,
58342C042AAB61FB003BA12D /* State+Extensions.swift in Sources */,
+ A98F1B452C19BB83003C869E /* NEPacketTunnelProvider+Extensions.swift in Sources */,
A95EEE382B722DFC00A8A39B /* PingStats.swift in Sources */,
583832272AC3193600EA2071 /* PacketTunnelActor+SleepCycle.swift in Sources */,
58FE25DC2AA72A8F003D1918 /* AnyTask.swift in Sources */,
@@ -5953,7 +6039,6 @@
58F3F36A2AA08E3C00D3B0A4 /* PacketTunnelProvider.swift in Sources */,
58906DE02445C7A5002F0673 /* NEProviderStopReason+Debug.swift in Sources */,
58C7A45B2A8640030060C66F /* PacketTunnelPathObserver.swift in Sources */,
- A948809B2BC9308D0090A44C /* PostQuantumKeyExchangeActor.swift in Sources */,
580D6B8E2AB33BBF00B2D6E0 /* BlockedStateErrorMapper.swift in Sources */,
06AC116228F94C450037AF9A /* ApplicationConfiguration.swift in Sources */,
583FE02429C1ACB3006E85F9 /* RESTCreateApplePaymentResponse+Localization.swift in Sources */,
@@ -6145,6 +6230,16 @@
files = (
A9630E492B921E6D00A65999 /* PacketTunnelProvider+TCPConnection.swift in Sources */,
A944F26A2B8DF32900473F4C /* PostQuantumKeyNegotiator.swift in Sources */,
+ A98F1B5A2C19C4C4003C869E /* PostQuantumKeyExchangeActor.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ A98F1B4A2C19C48D003C869E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ A98F1B512C19C48D003C869E /* PostQuantumKeyExchangeActorTests.swift in Sources */,
+ A9C3083A2C19DDA7008715F1 /* MullvadPostQuantum+Stubs.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -6368,6 +6463,17 @@
target = 58D223D4294C8E5E0029F5F8 /* MullvadTypes */;
targetProxy = A9630E412B8E10F700A65999 /* PBXContainerItemProxy */;
};
+ A98F1B472C19BCFF003C869E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 58C7A4352A863F440060C66F /* PacketTunnelCore */;
+ targetProxy = A98F1B462C19BCFF003C869E /* PBXContainerItemProxy */;
+ };
+ A98F1B542C19C48D003C869E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ platformFilter = ios;
+ target = A944F25B2B8DEFDB00473F4C /* MullvadPostQuantum */;
+ targetProxy = A98F1B532C19C48D003C869E /* PBXContainerItemProxy */;
+ };
A9EC20F22A5D79ED0040D56E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5840231E2A406BF5007B27AC /* TunnelObfuscation */;
@@ -8351,6 +8457,107 @@
};
name = MockRelease;
};
+ A98F1B562C19C48D003C869E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_STYLE = Manual;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.2;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadPostQuantumTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ A98F1B572C19C48D003C869E /* Staging */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_STYLE = Manual;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.2;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadPostQuantumTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Staging;
+ };
+ A98F1B582C19C48D003C869E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_STYLE = Manual;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.2;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadPostQuantumTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+ A98F1B592C19C48D003C869E /* MockRelease */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_STYLE = Manual;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.2;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadPostQuantumTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = MockRelease;
+ };
A9E99CE12B5195E600869AF2 /* MockRelease */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -9313,6 +9520,17 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ A98F1B552C19C48D003C869E /* Build configuration list for PBXNativeTarget "MullvadPostQuantumTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ A98F1B562C19C48D003C869E /* Debug */,
+ A98F1B572C19C48D003C869E /* Staging */,
+ A98F1B582C19C48D003C869E /* Release */,
+ A98F1B592C19C48D003C869E /* MockRelease */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
F0ACE3162BE4E479006D5333 /* Build configuration list for PBXNativeTarget "MullvadMockData" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -9401,6 +9619,11 @@
package = 58F097482A20C30000DA2DAD /* XCRemoteSwiftPackageReference "wireguard-apple" */;
productName = WireGuardKitTypes;
};
+ A959FC4D2C19CF70009733CD /* WireGuardKitTypes */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 58F097482A20C30000DA2DAD /* XCRemoteSwiftPackageReference "wireguard-apple" */;
+ productName = WireGuardKitTypes;
+ };
F0ACE3272BE4E712006D5333 /* WireGuardKitTypes */ = {
isa = XCSwiftPackageProductDependency;
package = 58F097482A20C30000DA2DAD /* XCRemoteSwiftPackageReference "wireguard-apple" */;
diff --git a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadPostQuantumTests.xcscheme b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadPostQuantumTests.xcscheme
new file mode 100644
index 0000000000..8f78b7481d
--- /dev/null
+++ b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadPostQuantumTests.xcscheme
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1520"
+ version = "1.7">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ shouldAutocreateTestPlan = "YES">
+ <Testables>
+ <TestableReference
+ skipped = "NO"
+ parallelizable = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "A98F1B4D2C19C48D003C869E"
+ BuildableName = "MullvadPostQuantumTests.xctest"
+ BlueprintName = "MullvadPostQuantumTests"
+ ReferencedContainer = "container:MullvadVPN.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme
index e96e9b561d..8e3306fc2b 100644
--- a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme
+++ b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme
@@ -146,6 +146,20 @@
ReferencedContainer = "container:MullvadVPN.xcodeproj">
</BuildableReference>
</BuildActionEntry>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "NO"
+ buildForProfiling = "NO"
+ buildForArchiving = "NO"
+ buildForAnalyzing = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "A98F1B4D2C19C48D003C869E"
+ BuildableName = "MullvadPostQuantumTests.xctest"
+ BlueprintName = "MullvadPostQuantumTests"
+ ReferencedContainer = "container:MullvadVPN.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
index aae7677d9a..ab464a2274 100644
--- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
+++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
@@ -35,7 +35,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
override init() {
Self.configureLogging()
-
providerLogger = Logger(label: "PacketTunnelProvider")
let containerURL = ApplicationConfiguration.containerURL
@@ -46,7 +45,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
relayCache: RelayCache(cacheDirectory: containerURL),
ipOverrideRepository: IPOverrideRepository()
)
-
multihopUpdater = MultihopUpdater(listener: multihopStateListener)
super.init()
@@ -100,7 +98,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
)
let urlRequestProxy = URLRequestProxy(dispatchQueue: internalQueue, transportProvider: transportProvider)
-
appMessageHandler = AppMessageHandler(packetTunnelActor: actor, urlRequestProxy: urlRequestProxy)
}
@@ -264,7 +261,6 @@ extension PacketTunnelProvider {
lastConnectionAttempt = connectionAttempt
case let .negotiatingPostQuantumKey(_, privateKey):
- postQuantumActor.endCurrentNegotiation()
postQuantumActor.startNegotiation(with: privateKey)
case .initial, .connected, .disconnecting, .disconnected, .error:
@@ -311,12 +307,11 @@ extension PacketTunnelProvider {
extension PacketTunnelProvider: PostQuantumKeyReceiving {
func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) {
+ postQuantumActor.reset()
actor.replacePreSharedKey(key, ephemeralKey: ephemeralKey)
- postQuantumActor.endCurrentNegotiation()
}
func keyExchangeFailed() {
- postQuantumActor.endCurrentNegotiation()
// Do not try reconnecting to the `.current` relay, else the actor's `State` equality check will fail
// and it will not try to reconnect
actor.reconnect(to: .random, reconnectReason: .connectionLoss)
diff --git a/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift
index 33291d8ea5..a9a13e2635 100644
--- a/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift
+++ b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift
@@ -7,13 +7,15 @@
//
import Foundation
-import MullvadPostQuantum
+import MullvadREST
+import MullvadTypes
import NetworkExtension
+import PacketTunnelCore
import WireGuardKitTypes
-class PostQuantumKeyExchangeActor {
+public class PostQuantumKeyExchangeActor {
struct Negotiation {
- var negotiator: PostQuantumKeyNegotiator
+ var negotiator: PostQuantumKeyNegotiating
var inTunnelTCPConnection: NWTCPConnection
var tcpConnectionObserver: NSKeyValueObservation
@@ -24,16 +26,26 @@ class PostQuantumKeyExchangeActor {
}
}
- unowned let packetTunnel: PacketTunnelProvider
- private var negotiation: Negotiation?
+ unowned let packetTunnel: any TunnelProvider
+ internal var negotiation: Negotiation?
private var timer: DispatchSourceTimer?
+ private var keyExchangeRetriesIterator: AnyIterator<Duration>
+ private let negotiationProvider: PostQuantumKeyNegotiating.Type
// Callback in the event of the negotiation failing on startup
var onFailure: () -> Void
- init(packetTunnel: PacketTunnelProvider, onFailure: @escaping (() -> Void)) {
+ public init(
+ packetTunnel: any TunnelProvider,
+ onFailure: @escaping (() -> Void),
+ negotiationProvider: PostQuantumKeyNegotiating.Type = PostQuantumKeyNegotiator.self,
+ keyExchangeRetriesIterator: AnyIterator<Duration> = REST.RetryStrategy.postQuantumKeyExchange
+ .makeDelayIterator()
+ ) {
self.packetTunnel = packetTunnel
self.onFailure = onFailure
+ self.negotiationProvider = negotiationProvider
+ self.keyExchangeRetriesIterator = keyExchangeRetriesIterator
}
private func createTCPConnection(_ gatewayEndpoint: NWHostEndpoint) -> NWTCPConnection {
@@ -45,8 +57,17 @@ class PostQuantumKeyExchangeActor {
)
}
- func startNegotiation(with privateKey: PrivateKey) {
- let negotiator = PostQuantumKeyNegotiator()
+ /// 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)!
@@ -56,8 +77,9 @@ class PostQuantumKeyExchangeActor {
// 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() + 10)
+ scheduleInTunnelConnectionTimeout(startTime: .now() + tcpConnectionTimeout)
let tcpConnectionObserver = inTunnelTCPConnection.observe(\.isViable, options: [
.initial,
@@ -72,7 +94,8 @@ class PostQuantumKeyExchangeActor {
devicePublicKey: privateKey.publicKey,
presharedKey: ephemeralSharedKey,
packetTunnel: packetTunnel,
- tcpConnection: inTunnelTCPConnection
+ tcpConnection: inTunnelTCPConnection,
+ postQuantumKeyExchangeTimeout: tcpConnectionTimeout
) {
self.negotiation = nil
self.onFailure()
@@ -85,11 +108,18 @@ class PostQuantumKeyExchangeActor {
)
}
- func endCurrentNegotiation() {
+ /// 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 = REST.RetryStrategy.postQuantumKeyExchange.makeDelayIterator()
+ endCurrentNegotiation()
+ }
+
private func scheduleInTunnelConnectionTimeout(startTime: DispatchWallTime) {
let newTimer = DispatchSource.makeTimerSource()
diff --git a/ios/PacketTunnelCore/NEPacketTunnelProvider+Extensions.swift b/ios/PacketTunnelCore/NEPacketTunnelProvider+Extensions.swift
new file mode 100644
index 0000000000..9961da4beb
--- /dev/null
+++ b/ios/PacketTunnelCore/NEPacketTunnelProvider+Extensions.swift
@@ -0,0 +1,21 @@
+//
+// NEPacketTunnelProvider+Extensions.swift
+// PacketTunnelCore
+//
+// Created by Marco Nikic on 2024-06-12.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import NetworkExtension
+
+public protocol TunnelProvider: AnyObject {
+ func createTCPConnectionThroughTunnel(
+ to remoteEndpoint: NWEndpoint,
+ enableTLS: Bool,
+ tlsParameters TLSParameters: NWTLSParameters?,
+ delegate: Any?
+ ) -> NWTCPConnection
+}
+
+extension NEPacketTunnelProvider: TunnelProvider {}
diff --git a/ios/TestPlans/MullvadVPNApp.xctestplan b/ios/TestPlans/MullvadVPNApp.xctestplan
index ba94d95b98..e86ac7a2d2 100644
--- a/ios/TestPlans/MullvadVPNApp.xctestplan
+++ b/ios/TestPlans/MullvadVPNApp.xctestplan
@@ -64,6 +64,13 @@
"identifier" : "7A88DCD62A8FABBE00D2FF0E",
"name" : "RoutingTests"
}
+ },
+ {
+ "target" : {
+ "containerPath" : "container:MullvadVPN.xcodeproj",
+ "identifier" : "A98F1B4D2C19C48D003C869E",
+ "name" : "MullvadPostQuantumTests"
+ }
}
],
"version" : 1
diff --git a/ios/TestPlans/MullvadVPNCI.xctestplan b/ios/TestPlans/MullvadVPNCI.xctestplan
index f71d07ccfd..4cbc9ab731 100644
--- a/ios/TestPlans/MullvadVPNCI.xctestplan
+++ b/ios/TestPlans/MullvadVPNCI.xctestplan
@@ -64,6 +64,13 @@
"identifier" : "7A88DCD62A8FABBE00D2FF0E",
"name" : "RoutingTests"
}
+ },
+ {
+ "target" : {
+ "containerPath" : "container:MullvadVPN.xcodeproj",
+ "identifier" : "A98F1B4D2C19C48D003C869E",
+ "name" : "MullvadPostQuantumTests"
+ }
}
],
"version" : 1
diff --git a/talpid-tunnel-config-client/src/ios_ffi/ios_runtime.rs b/talpid-tunnel-config-client/src/ios_ffi/ios_runtime.rs
index 37fdb0d16d..1ab08a19bc 100644
--- a/talpid-tunnel-config-client/src/ios_ffi/ios_runtime.rs
+++ b/talpid-tunnel-config-client/src/ios_ffi/ios_runtime.rs
@@ -1,9 +1,15 @@
use super::{ios_tcp_connection::*, PostQuantumCancelToken};
use crate::{request_ephemeral_peer_with, Error, RelayConfigService};
use libc::c_void;
-use std::{future::Future, io, pin::Pin, ptr, sync::Arc};
+use std::{
+ future::Future,
+ io,
+ pin::Pin,
+ ptr,
+ sync::{Arc, Mutex},
+};
use talpid_types::net::wireguard::{PrivateKey, PublicKey};
-use tokio::{runtime::Builder, sync::mpsc};
+use tokio::runtime::Builder;
use tonic::transport::channel::Endpoint;
use tower::util::service_fn;
@@ -15,10 +21,19 @@ pub unsafe fn run_post_quantum_psk_exchange(
ephemeral_key: [u8; 32],
packet_tunnel: *const c_void,
tcp_connection: *const c_void,
+ post_quantum_key_exchange_timeout: u64,
) -> Result<PostQuantumCancelToken, Error> {
- match unsafe { IOSRuntime::new(pub_key, ephemeral_key, packet_tunnel, tcp_connection) } {
+ match unsafe {
+ IOSRuntime::new(
+ pub_key,
+ ephemeral_key,
+ packet_tunnel,
+ tcp_connection,
+ post_quantum_key_exchange_timeout,
+ )
+ } {
Ok(runtime) => {
- let token = runtime.cancel_token_tx.clone();
+ let token = runtime.packet_tunnel.tcp_connection.clone();
runtime.run();
Ok(PostQuantumCancelToken {
@@ -35,7 +50,7 @@ pub unsafe fn run_post_quantum_psk_exchange(
#[derive(Clone)]
pub struct SwiftContext {
pub packet_tunnel: *const c_void,
- pub tcp_connection: *const c_void,
+ pub tcp_connection: Arc<Mutex<ConnectionContext>>,
}
unsafe impl Send for SwiftContext {}
@@ -46,8 +61,7 @@ struct IOSRuntime {
pub_key: [u8; 32],
ephemeral_key: [u8; 32],
packet_tunnel: SwiftContext,
- cancel_token_tx: Arc<mpsc::UnboundedSender<()>>,
- cancel_token_rx: mpsc::UnboundedReceiver<()>,
+ post_quantum_key_exchange_timeout: u64,
}
impl IOSRuntime {
@@ -56,6 +70,7 @@ impl IOSRuntime {
ephemeral_key: [u8; 32],
packet_tunnel: *const libc::c_void,
tcp_connection: *const c_void,
+ post_quantum_key_exchange_timeout: u64,
) -> io::Result<Self> {
let runtime = Builder::new_multi_thread()
.enable_all()
@@ -64,18 +79,15 @@ impl IOSRuntime {
let context = SwiftContext {
packet_tunnel,
- tcp_connection,
+ tcp_connection: Arc::new(Mutex::new(ConnectionContext::new(tcp_connection))),
};
- let (tx, rx) = mpsc::unbounded_channel();
-
Ok(Self {
runtime,
pub_key,
ephemeral_key,
packet_tunnel: context,
- cancel_token_tx: Arc::new(tx),
- cancel_token_rx: rx,
+ post_quantum_key_exchange_timeout,
})
}
@@ -84,7 +96,6 @@ impl IOSRuntime {
self.run_service_inner();
});
}
-
/// Creates a `RelayConfigService` using the in-tunnel TCP Connection provided by the Packet Tunnel Provider
/// # Safety
/// It is unsafe to call this with an already used `SwiftContext`
@@ -112,11 +123,7 @@ impl IOSRuntime {
}
fn run_service_inner(self) {
- let Self {
- runtime,
- mut cancel_token_rx,
- ..
- } = self;
+ let Self { runtime, .. } = self;
let packet_tunnel_ptr = self.packet_tunnel.packet_tunnel;
runtime.block_on(async move {
@@ -146,21 +153,27 @@ impl IOSRuntime {
let preshared_key_bytes = preshared_key.as_bytes();
swift_post_quantum_key_ready(packet_tunnel_ptr, preshared_key_bytes.as_ptr(), self.ephemeral_key.as_ptr());
},
- None => unsafe {
- swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null());
+ None => {
+ log::error!("No suitable peer was found");
+ unsafe {
+ swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null());
+ }
}
}
},
- Err(_) => unsafe {
- swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null());
+ Err(error) => {
+ log::error!("Key exchange failed {}", error);
+ unsafe {
+ swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null());
+ }
}
}
}
- _ = cancel_token_rx.recv() => {
- shutdown_handle.shutdown()
- // The swift runtime pre emptively cancelled the key exchange, nothing to do here.
+ _ = tokio::time::sleep(std::time::Duration::from_secs(self.post_quantum_key_exchange_timeout)) => {
+ shutdown_handle.shutdown();
+ unsafe { swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null()); }
}
}
});
diff --git a/talpid-tunnel-config-client/src/ios_ffi/ios_tcp_connection.rs b/talpid-tunnel-config-client/src/ios_ffi/ios_tcp_connection.rs
index 86a3114edf..b550cc69b3 100644
--- a/talpid-tunnel-config-client/src/ios_ffi/ios_tcp_connection.rs
+++ b/talpid-tunnel-config-client/src/ios_ffi/ios_tcp_connection.rs
@@ -1,10 +1,7 @@
use libc::c_void;
use std::{
io::{self, Result},
- sync::{
- atomic::{self, AtomicBool},
- Arc, Mutex, Weak,
- },
+ sync::{Arc, Mutex, MutexGuard, Weak},
task::{Poll, Waker},
};
use tokio::{
@@ -49,28 +46,29 @@ pub struct IosTcpProvider {
write_rx: mpsc::UnboundedReceiver<usize>,
read_tx: Arc<mpsc::UnboundedSender<Box<[u8]>>>,
read_rx: mpsc::UnboundedReceiver<Box<[u8]>>,
- tcp_connection: *const c_void,
+ tcp_connection: Arc<Mutex<ConnectionContext>>,
read_in_progress: bool,
write_in_progress: bool,
- shutdown: Arc<AtomicBool>,
- waker: Arc<Mutex<Option<Waker>>>,
}
pub struct IosTcpShutdownHandle {
- shutdown: Arc<AtomicBool>,
- waker: Arc<Mutex<Option<Waker>>>,
+ context: Arc<Mutex<ConnectionContext>>,
}
+pub struct ConnectionContext {
+ waker: Option<Waker>,
+ tcp_connection: Option<*const c_void>,
+}
+
+unsafe impl Send for ConnectionContext {}
+
impl IosTcpProvider {
- /**
- * # Safety
- * `tcp_connection` must be pointing to a valid instance of a `NWTCPConnection`, created by the `PacketTunnelProvider`
- */
- pub unsafe fn new(tcp_connection: *const c_void) -> (Self, IosTcpShutdownHandle) {
+ /// # Safety
+ /// `connection` must be pointing to a valid instance of a `NWTCPConnection`, created by the
+ /// `PacketTunnelProvider`
+ pub unsafe fn new(connection: Arc<Mutex<ConnectionContext>>) -> (Self, IosTcpShutdownHandle) {
let (tx, rx) = mpsc::unbounded_channel();
let (recv_tx, recv_rx) = mpsc::unbounded_channel();
- let shutdown = Arc::new(AtomicBool::new(false));
- let waker = Arc::new(Mutex::new(None));
(
Self {
@@ -78,33 +76,32 @@ impl IosTcpProvider {
write_rx: rx,
read_tx: Arc::new(recv_tx),
read_rx: recv_rx,
- tcp_connection,
+ tcp_connection: connection.clone(),
read_in_progress: false,
write_in_progress: false,
- shutdown: shutdown.clone(),
- waker: waker.clone(),
},
- IosTcpShutdownHandle { shutdown, waker },
+ IosTcpShutdownHandle {
+ context: connection,
+ },
)
}
- fn is_shutdown(&self) -> bool {
- self.shutdown.load(atomic::Ordering::SeqCst)
- }
-
- fn maybe_set_waker(&self, new_waker: Waker) {
- if let Ok(mut waker) = self.waker.lock() {
- *waker = Some(new_waker);
- }
+ fn maybe_set_waker(new_waker: Waker, connection: &mut MutexGuard<'_, ConnectionContext>) {
+ connection.waker = Some(new_waker);
}
}
impl IosTcpShutdownHandle {
- pub fn shutdown(&self) {
- self.shutdown.store(true, atomic::Ordering::SeqCst);
- if let Some(waker) = self.waker.lock().ok().and_then(|mut waker| waker.take()) {
+ pub fn shutdown(self) {
+ let Ok(mut context) = self.context.lock() else {
+ return;
+ };
+
+ context.tcp_connection = None;
+ if let Some(waker) = context.waker.take() {
waker.wake();
}
+ std::mem::drop(context);
}
}
@@ -114,7 +111,15 @@ impl AsyncWrite for IosTcpProvider {
cx: &mut std::task::Context<'_>,
buf: &[u8],
) -> std::task::Poll<Result<usize>> {
- self.maybe_set_waker(cx.waker().clone());
+ let connection_lock = self.tcp_connection.clone();
+ let Ok(mut connection) = connection_lock.lock() else {
+ return Poll::Ready(Err(connection_closed_err()));
+ };
+ let Some(tcp_ptr) = connection.tcp_connection else {
+ return Poll::Ready(Err(connection_closed_err()));
+ };
+ Self::maybe_set_waker(cx.waker().clone(), &mut connection);
+
match self.write_rx.poll_recv(cx) {
std::task::Poll::Ready(Some(bytes_sent)) => {
self.write_in_progress = false;
@@ -125,14 +130,11 @@ impl AsyncWrite for IosTcpProvider {
Poll::Ready(Err(connection_closed_err()))
}
std::task::Poll::Pending => {
- if self.is_shutdown() {
- return Poll::Ready(Err(connection_closed_err()));
- }
if !self.write_in_progress {
let raw_sender = Weak::into_raw(Arc::downgrade(&self.write_tx));
unsafe {
swift_nw_tcp_connection_send(
- self.tcp_connection,
+ tcp_ptr,
buf.as_ptr() as _,
buf.len(),
raw_sender as _,
@@ -165,9 +167,14 @@ impl AsyncRead for IosTcpProvider {
cx: &mut std::task::Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> std::task::Poll<std::io::Result<()>> {
- if self.is_shutdown() {
+ let connection_lock = self.tcp_connection.clone();
+ let Ok(mut connection) = connection_lock.lock() else {
return Poll::Ready(Err(connection_closed_err()));
- }
+ };
+ let Some(tcp_ptr) = connection.tcp_connection else {
+ return Poll::Ready(Err(connection_closed_err()));
+ };
+ Self::maybe_set_waker(cx.waker().clone(), &mut connection);
match self.read_rx.poll_recv(cx) {
std::task::Poll::Ready(Some(data)) => {
@@ -183,7 +190,7 @@ impl AsyncRead for IosTcpProvider {
if !self.read_in_progress {
let raw_sender = Weak::into_raw(Arc::downgrade(&self.read_tx));
unsafe {
- swift_nw_tcp_connection_read(self.tcp_connection, raw_sender as _);
+ swift_nw_tcp_connection_read(tcp_ptr, raw_sender as _);
}
self.read_in_progress = true;
}
@@ -192,3 +199,19 @@ impl AsyncRead for IosTcpProvider {
}
}
}
+
+impl ConnectionContext {
+ pub fn new(tcp_connection: *const c_void) -> Self {
+ Self {
+ tcp_connection: Some(tcp_connection),
+ waker: None,
+ }
+ }
+
+ pub fn shutdown(&mut self) {
+ self.tcp_connection = None;
+ if let Some(waker) = self.waker.take() {
+ waker.wake();
+ }
+ }
+}
diff --git a/talpid-tunnel-config-client/src/ios_ffi/mod.rs b/talpid-tunnel-config-client/src/ios_ffi/mod.rs
index 94dcf95b6f..704332a7bf 100644
--- a/talpid-tunnel-config-client/src/ios_ffi/mod.rs
+++ b/talpid-tunnel-config-client/src/ios_ffi/mod.rs
@@ -2,8 +2,9 @@ pub mod ios_runtime;
pub mod ios_tcp_connection;
use crate::ios_ffi::ios_runtime::run_post_quantum_psk_exchange;
+use ios_tcp_connection::ConnectionContext;
use libc::c_void;
-use std::sync::{Arc, Weak};
+use std::sync::{Arc, Mutex, Weak};
use tokio::sync::mpsc;
use std::sync::Once;
@@ -20,34 +21,43 @@ impl PostQuantumCancelToken {
/// This function can only be called when the context pointer is valid.
unsafe fn cancel(&self) {
// # Safety
- // Try to take the value, if there is a value, we can safely send the message, otherwise, assume it has been dropped and nothing happens
- let send_tx: Arc<mpsc::UnboundedSender<()>> = unsafe { Arc::from_raw(self.context as _) };
- let _ = send_tx.send(());
+ // Try to take the value, if there is a value, we can safely send the message, otherwise,
+ // assume it has been dropped and nothing happens
+ let connection_context: Arc<Mutex<ConnectionContext>> =
+ unsafe { Arc::from_raw(self.context as _) };
+ if let Ok(mut connection) = connection_context.lock() {
+ connection.shutdown();
+ }
+
// Call std::mem::forget here to avoid dropping the channel.
- std::mem::forget(send_tx);
+ std::mem::forget(connection_context);
}
}
impl Drop for PostQuantumCancelToken {
fn drop(&mut self) {
- let _: Arc<mpsc::UnboundedSender<()>> = unsafe { Arc::from_raw(self.context as _) };
+ let _: Arc<Mutex<ConnectionContext>> = unsafe { Arc::from_raw(self.context as _) };
}
}
+
unsafe impl Send for PostQuantumCancelToken {}
/// Called by the Swift side to signal that the quantum-secure key exchange should be cancelled.
///
/// # Safety
-/// `sender` must be pointing to a valid instance of a `PostQuantumCancelToken` created by the `PacketTunnelProvider`.
+/// `sender` must be pointing to a valid instance of a `PostQuantumCancelToken` created by the
+/// `PacketTunnelProvider`.
#[no_mangle]
pub unsafe extern "C" fn cancel_post_quantum_key_exchange(sender: *const PostQuantumCancelToken) {
let sender = unsafe { &*sender };
sender.cancel();
}
-/// Called by the Swift side to signal that the Rust `PostQuantumCancelToken` can be safely dropped from memory.
+/// Called by the Swift side to signal that the Rust `PostQuantumCancelToken` can be safely dropped
+/// from memory.
///
/// # Safety
-/// `sender` must be pointing to a valid instance of a `PostQuantumCancelToken` created by the `PacketTunnelProvider`.
+/// `sender` must be pointing to a valid instance of a `PostQuantumCancelToken` created by the
+/// `PacketTunnelProvider`.
#[no_mangle]
pub unsafe extern "C" fn drop_post_quantum_key_exchange_token(
sender: *const PostQuantumCancelToken,
@@ -74,8 +84,8 @@ pub unsafe extern "C" fn handle_sent(bytes_sent: usize, sender: *const c_void) {
/// Called by Swift whenever data has been read from the in-tunnel TCP connection when exchanging
/// quantum-resistant pre shared keys.
///
-/// 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.
+/// 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`
@@ -102,8 +112,8 @@ pub unsafe extern "C" fn handle_recv(data: *const u8, mut data_len: usize, sende
/// # 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.
+/// `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.
#[no_mangle]
pub unsafe extern "C" fn negotiate_post_quantum_key(
@@ -112,10 +122,11 @@ pub unsafe extern "C" fn negotiate_post_quantum_key(
packet_tunnel: *const c_void,
tcp_connection: *const c_void,
cancel_token: *mut PostQuantumCancelToken,
+ post_quantum_key_exchange_timeout: u64,
) -> i32 {
INIT_LOGGING.call_once(|| {
let _ = oslog::OsLogger::new("net.mullvad.MullvadVPN.TTCC")
- .level_filter(log::LevelFilter::Trace)
+ .level_filter(log::LevelFilter::Debug)
.init();
});
@@ -123,7 +134,13 @@ pub unsafe extern "C" fn negotiate_post_quantum_key(
let eph_key_copy: [u8; 32] = unsafe { std::ptr::read(ephemeral_key as *const [u8; 32]) };
match unsafe {
- run_post_quantum_psk_exchange(pub_key_copy, eph_key_copy, packet_tunnel, tcp_connection)
+ run_post_quantum_psk_exchange(
+ pub_key_copy,
+ eph_key_copy,
+ packet_tunnel,
+ tcp_connection,
+ post_quantum_key_exchange_timeout,
+ )
} {
Ok(token) => {
unsafe { std::ptr::write(cancel_token, token) };