summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorEmīls <emils@mullvad.net>2024-05-21 17:04:43 +0200
committerEmīls <emils@mullvad.net>2024-05-21 17:04:43 +0200
commit2211f2875fc7953eea2ec2caa04f03d91f58be49 (patch)
tree740bb2e438864e0f273bc724658048170a8416fd
parentcba171ca2232838b3e7628221ed1b5a56f93230f (diff)
parent0c682760f4a7f8b2852f3212c98214b11c916661 (diff)
downloadmullvadvpn-2211f2875fc7953eea2ec2caa04f03d91f58be49.tar.xz
mullvadvpn-2211f2875fc7953eea2ec2caa04f03d91f58be49.zip
Merge remote-tracking branch 'origin/features/ios-post-quantum'
-rw-r--r--.github/workflows/ios.yml5
-rw-r--r--Cargo.lock2
-rw-r--r--ios/MullvadPostQuantum/MullvadPostQuantum.h10
-rw-r--r--ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift114
-rw-r--r--ios/MullvadPostQuantum/PostQuantumKeyNegotiator.swift56
-rw-r--r--ios/MullvadPostQuantum/module.private.modulemap5
-rw-r--r--ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h96
-rw-r--r--ios/MullvadSettings/QuantumResistanceSettings.swift7
-rw-r--r--ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift12
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj428
-rw-r--r--ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadPostQuantum.xcscheme66
-rw-r--r--ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift8
-rw-r--r--ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelProviderHost.swift6
-rw-r--r--ios/MullvadVPN/TunnelManager/MapConnectionStatusOperation.swift12
-rw-r--r--ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift5
-rw-r--r--ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift8
-rw-r--r--ios/MullvadVPN/TunnelManager/TunnelManager.swift7
-rw-r--r--ios/MullvadVPN/TunnelManager/TunnelState+UI.swift259
-rw-r--r--ios/MullvadVPN/TunnelManager/TunnelState.swift43
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift232
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift13
-rw-r--r--ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift2
-rw-r--r--ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift26
-rw-r--r--ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelStateTests.swift139
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift40
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider/SettingsReader.swift3
-rw-r--r--ios/PacketTunnel/PostQuantumKeyExchangeActor.swift107
-rw-r--r--ios/PacketTunnelCore/Actor/ConfigurationBuilder.swift24
-rw-r--r--ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift14
-rw-r--r--ios/PacketTunnelCore/Actor/ObservedState.swift15
-rw-r--r--ios/PacketTunnelCore/Actor/PacketTunnelActor+ConnectionMonitoring.swift4
-rw-r--r--ios/PacketTunnelCore/Actor/PacketTunnelActor+ErrorState.swift3
-rw-r--r--ios/PacketTunnelCore/Actor/PacketTunnelActor+PostQuantum.swift84
-rw-r--r--ios/PacketTunnelCore/Actor/PacketTunnelActor+Public.swift4
-rw-r--r--ios/PacketTunnelCore/Actor/PacketTunnelActor.swift79
-rw-r--r--ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift2
-rw-r--r--ios/PacketTunnelCore/Actor/Protocols/RelaySelectorProtocol.swift6
-rw-r--r--ios/PacketTunnelCore/Actor/Protocols/SettingsReaderProtocol.swift6
-rw-r--r--ios/PacketTunnelCore/Actor/State+Extensions.swift9
-rw-r--r--ios/PacketTunnelCore/Actor/State.swift6
-rw-r--r--ios/PacketTunnelCoreTests/Mocks/SettingsReaderStub.swift3
-rw-r--r--ios/PacketTunnelCoreTests/PacketTunnelActorTests.swift3
-rw-r--r--ios/PacketTunnelCoreTests/ProtocolObfuscatorTests.swift19
-rw-r--r--ios/build-rust-library.sh20
-rw-r--r--talpid-tunnel-config-client/Cargo.toml26
-rw-r--r--talpid-tunnel-config-client/build.rs12
-rw-r--r--talpid-tunnel-config-client/src/ios_ffi/ios_runtime.rs168
-rw-r--r--talpid-tunnel-config-client/src/ios_ffi/ios_tcp_connection.rs194
-rw-r--r--talpid-tunnel-config-client/src/ios_ffi/mod.rs134
-rw-r--r--talpid-tunnel-config-client/src/lib.rs119
50 files changed, 2198 insertions, 467 deletions
diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml
index f0c12323c3..e1ab5c4b74 100644
--- a/.github/workflows/ios.yml
+++ b/.github/workflows/ios.yml
@@ -93,6 +93,11 @@ jobs:
brew update
brew install xcbeautify
+ - name: Install protobuf
+ run: |
+ brew update
+ brew install protobuf
+
- name: Run unit tests
run: |
set -o pipefail && env NSUnbufferedIO=YES xcodebuild \
diff --git a/Cargo.lock b/Cargo.lock
index 34c23cef16..07fb64dcd9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4070,9 +4070,11 @@ dependencies = [
name = "talpid-tunnel-config-client"
version = "0.0.0"
dependencies = [
+ "cbindgen",
"classic-mceliece-rust",
"libc",
"log",
+ "oslog",
"pqc_kyber",
"prost",
"rand 0.8.5",
diff --git a/ios/MullvadPostQuantum/MullvadPostQuantum.h b/ios/MullvadPostQuantum/MullvadPostQuantum.h
new file mode 100644
index 0000000000..c196b0527b
--- /dev/null
+++ b/ios/MullvadPostQuantum/MullvadPostQuantum.h
@@ -0,0 +1,10 @@
+//
+// MullvadPostQuantum.h
+// MullvadPostQuantum
+//
+// Created by Marco Nikic on 2024-02-27.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "talpid_tunnel_config_client.h"
diff --git a/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift
new file mode 100644
index 0000000000..b3bf815b1b
--- /dev/null
+++ b/ios/MullvadPostQuantum/PacketTunnelProvider+TCPConnection.swift
@@ -0,0 +1,114 @@
+//
+// PacketTunnelProvider+TCPConnection.swift
+// PacketTunnel
+//
+// Created by Marco Nikic on 2024-02-15.
+// Copyright © 2023 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import MullvadTypes
+import NetworkExtension
+import TalpidTunnelConfigClientProxy
+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 a quantum-secure pre shared key exchange.
+///
+/// This FFI function is called by Rust when the quantum-secure pre shared key exchange has either failed, or succeeded.
+/// When both the `rawPresharedKey` and the `rawEphemeralKey` are raw pointers to 32 bytes data arrays,
+/// the quantum-secure key exchange is considered successful. In any other case, the exchange is considered failed.
+///
+/// - Parameters:
+/// - rawPacketTunnel: 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_post_quantum_key_ready")
+func receivePostQuantumKey(
+ rawPacketTunnel: UnsafeMutableRawPointer?,
+ rawPresharedKey: UnsafeMutableRawPointer?,
+ rawEphemeralKey: UnsafeMutableRawPointer?
+) {
+ guard
+ let rawPacketTunnel,
+ let postQuantumKeyReceiver = Unmanaged<NEPacketTunnelProvider>.fromOpaque(rawPacketTunnel)
+ .takeUnretainedValue() as? PostQuantumKeyReceiving
+ else { return }
+
+ guard
+ let rawPresharedKey,
+ let rawEphemeralKey,
+ let ephemeralKey = PrivateKey(rawValue: Data(bytes: rawEphemeralKey, count: 32)),
+ let key = PreSharedKey(rawValue: Data(bytes: rawPresharedKey, count: 32))
+ else {
+ postQuantumKeyReceiver.keyExchangeFailed()
+ return
+ }
+
+ postQuantumKeyReceiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey)
+}
diff --git a/ios/MullvadPostQuantum/PostQuantumKeyNegotiator.swift b/ios/MullvadPostQuantum/PostQuantumKeyNegotiator.swift
new file mode 100644
index 0000000000..16f7a24931
--- /dev/null
+++ b/ios/MullvadPostQuantum/PostQuantumKeyNegotiator.swift
@@ -0,0 +1,56 @@
+//
+// PostQuantumKeyNegotiator.swift
+// PacketTunnel
+//
+// Created by Marco Nikic on 2024-02-16.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import NetworkExtension
+import TalpidTunnelConfigClientProxy
+import WireGuardKitTypes
+
+/**
+ Attempt to start the asynchronous process of key negotiation. Returns true if successfully started, false if failed.
+ */
+public class PostQuantumKeyNegotiator {
+ public init() {}
+
+ var cancelToken: PostQuantumCancelToken?
+
+ public func startNegotiation(
+ gatewayIP: IPv4Address,
+ devicePublicKey: PublicKey,
+ presharedKey: PrivateKey,
+ packetTunnel: NEPacketTunnelProvider,
+ tcpConnection: NWTCPConnection
+ ) -> Bool {
+ let packetTunnelPointer = Unmanaged.passUnretained(packetTunnel).toOpaque()
+ let opaqueConnection = Unmanaged.passUnretained(tcpConnection).toOpaque()
+ var cancelToken = PostQuantumCancelToken()
+
+ let result = negotiate_post_quantum_key(
+ devicePublicKey.rawValue.map { $0 },
+ presharedKey.rawValue.map { $0 },
+ packetTunnelPointer,
+ opaqueConnection,
+ &cancelToken
+ )
+ guard result == 0 else {
+ return false
+ }
+ self.cancelToken = cancelToken
+ return true
+ }
+
+ public func cancelKeyNegotiation() {
+ guard var cancelToken else { return }
+ cancel_post_quantum_key_exchange(&cancelToken)
+ }
+
+ deinit {
+ guard var cancelToken else { return }
+ drop_post_quantum_key_exchange_token(&cancelToken)
+ }
+}
diff --git a/ios/MullvadPostQuantum/module.private.modulemap b/ios/MullvadPostQuantum/module.private.modulemap
new file mode 100644
index 0000000000..f831c7ca2e
--- /dev/null
+++ b/ios/MullvadPostQuantum/module.private.modulemap
@@ -0,0 +1,5 @@
+framework module TalpidTunnelConfigClientProxy {
+ header "talpid_tunnel_config_client.h"
+ link "libtalpid_tunnel_config_client"
+ export *
+}
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
new file mode 100644
index 0000000000..04180db289
--- /dev/null
+++ b/ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h
@@ -0,0 +1,96 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+/**
+ * Port used by the tunnel config service.
+ */
+#define CONFIG_SERVICE_PORT 1337
+
+typedef struct PostQuantumCancelToken {
+ void *context;
+} 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`.
+ */
+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.
+ *
+ * # Safety
+ * `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);
+
+/**
+ * Called by Swift whenever data has been written to the in-tunnel TCP connection when exchanging
+ * quantum-resistant pre shared keys.
+ *
+ * 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.
+ *
+ * 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);
+
+/**
+ * Entry point for exchanging post quantum keys on iOS.
+ * The TCP connection must be created to go through the tunnel.
+ * # 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 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);
+
+/**
+ * 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.
+ */
+extern void swift_nw_tcp_connection_read(const void *connection, const void *sender);
+
+/**
+ * Called when the preshared post quantum key is ready.
+ * `raw_preshared_key` might be NULL if the key negotiation failed.
+ */
+extern void swift_post_quantum_key_ready(const void *raw_packet_tunnel,
+ const uint8_t *raw_preshared_key,
+ const uint8_t *raw_ephemeral_private_key);
diff --git a/ios/MullvadSettings/QuantumResistanceSettings.swift b/ios/MullvadSettings/QuantumResistanceSettings.swift
index b5c12ae703..956b2fd0de 100644
--- a/ios/MullvadSettings/QuantumResistanceSettings.swift
+++ b/ios/MullvadSettings/QuantumResistanceSettings.swift
@@ -13,3 +13,10 @@ public enum TunnelQuantumResistance: Codable {
case on
case off
}
+
+public extension TunnelQuantumResistance {
+ /// A single source of truth for whether the current state counts as on
+ var isEnabled: Bool {
+ self == .on
+ }
+}
diff --git a/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift b/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift
index eb696fcca3..50809c50b1 100644
--- a/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift
+++ b/ios/MullvadTypes/Protocols/PostQuantumKeyReceiving.swift
@@ -10,18 +10,10 @@ import Foundation
import WireGuardKitTypes
public protocol PostQuantumKeyReceiving {
- func receivePostQuantumKey(_ key: PreSharedKey)
+ func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey)
+ func keyExchangeFailed()
}
public enum PostQuantumKeyReceivingError: Error {
case invalidKey
}
-
-public extension PostQuantumKeyReceiving {
- func receivePostQuantumKey(_ keyData: Data) throws {
- guard let key = PreSharedKey(rawValue: keyData) else {
- throw PostQuantumKeyReceivingError.invalidKey
- }
- receivePostQuantumKey(key)
- }
-}
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 776943004b..7b2bc1a714 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -44,10 +44,13 @@
449EBA262B975B9700DFA4EB /* PostQuantumKeyReceiving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449EBA252B975B9700DFA4EB /* PostQuantumKeyReceiving.swift */; };
44B02E3B2BC5732D008EDF34 /* LoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44B02E3A2BC5732D008EDF34 /* LoggingTests.swift */; };
44B02E3C2BC5B8A5008EDF34 /* Bundle+ProductVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5891BF1B25E3E3EB006D6FB0 /* Bundle+ProductVersion.swift */; };
+ 44BB5F972BE527F4002520EB /* TunnelState+UI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44BB5F962BE527F4002520EB /* TunnelState+UI.swift */; };
+ 44BB5F982BE527F4002520EB /* TunnelState+UI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44BB5F962BE527F4002520EB /* TunnelState+UI.swift */; };
44DD7D242B6CFFD70005F67F /* StartTunnelOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DD7D232B6CFFD70005F67F /* StartTunnelOperationTests.swift */; };
44DD7D272B6D18FB0005F67F /* MockTunnelInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DD7D262B6D18FB0005F67F /* MockTunnelInteractor.swift */; };
44DD7D292B7113CA0005F67F /* MockTunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DD7D282B7113CA0005F67F /* MockTunnel.swift */; };
44DD7D2D2B74E44A0005F67F /* QuantumResistanceSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DD7D2C2B74E44A0005F67F /* QuantumResistanceSettings.swift */; };
+ 44DF8AC42BF20BD200869CA4 /* PacketTunnelActor+PostQuantum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DF8AC32BF20BD200869CA4 /* PacketTunnelActor+PostQuantum.swift */; };
5803B4B02940A47300C23744 /* TunnelConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5803B4AF2940A47300C23744 /* TunnelConfiguration.swift */; };
5803B4B22940A48700C23744 /* TunnelStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5803B4B12940A48700C23744 /* TunnelStore.swift */; };
5807E2C02432038B00F5FF30 /* String+Split.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5807E2BF2432038B00F5FF30 /* String+Split.swift */; };
@@ -683,15 +686,26 @@
A917352129FAAA5200D5DCFD /* TransportStrategyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A917352029FAAA5200D5DCFD /* TransportStrategyTests.swift */; };
A91D78E32B03BDF200FCD5D3 /* TunnelObfuscation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5840231F2A406BF5007B27AC /* TunnelObfuscation.framework */; };
A91D78E42B03C01600FCD5D3 /* MullvadSettings.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58B2FDD32AA71D2A003EB5C6 /* MullvadSettings.framework */; };
+ A9259FD22B8E06E90032C82B /* MullvadPostQuantum.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
A93181A12B727ED700E341D2 /* TunnelSettingsV4.swift in Sources */ = {isa = PBXBuildFile; fileRef = A93181A02B727ED700E341D2 /* TunnelSettingsV4.swift */; };
A932D9EF2B5ADD0700999395 /* ProxyConfigurationTransportProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A932D9EE2B5ADD0700999395 /* ProxyConfigurationTransportProvider.swift */; };
A932D9F32B5EB61100999395 /* HeadRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A932D9F22B5EB61100999395 /* HeadRequestTests.swift */; };
A932D9F52B5EBB9D00999395 /* RESTTransportStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = A932D9F42B5EBB9D00999395 /* RESTTransportStub.swift */; };
A935594C2B4C2DA900D5D524 /* APIAvailabilityTestRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A935594B2B4C2DA900D5D524 /* APIAvailabilityTestRequest.swift */; };
+ A944F25F2B8DEFDB00473F4C /* MullvadPostQuantum.h in Headers */ = {isa = PBXBuildFile; fileRef = A944F25E2B8DEFDB00473F4C /* MullvadPostQuantum.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ A944F2622B8DEFDB00473F4C /* MullvadPostQuantum.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */; };
+ 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 */; };
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, ); }; };
+ A9630E432B8E10FB00A65999 /* MullvadTypes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223D5294C8E5E0029F5F8 /* MullvadTypes.framework */; };
+ A9630E442B8E115A00A65999 /* MullvadPostQuantum.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */; };
+ A9630E492B921E6D00A65999 /* PacketTunnelProvider+TCPConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9A557F42B7E3E5C0017ADA8 /* PacketTunnelProvider+TCPConnection.swift */; };
A970C89D2B29E38C000A7684 /* Socks5UsernamePasswordCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = A970C89C2B29E38C000A7684 /* Socks5UsernamePasswordCommand.swift */; };
A97D25AE2B0BB18100946B2D /* ProtocolObfuscator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97D25AD2B0BB18100946B2D /* ProtocolObfuscator.swift */; };
A97D25B02B0BB5C400946B2D /* ProtocolObfuscationStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97D25AF2B0BB5C400946B2D /* ProtocolObfuscationStub.swift */; };
@@ -814,6 +828,7 @@
A9BFB0012BD00B7F00F2BCA1 /* CustomListPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BFB0002BD00B7F00F2BCA1 /* CustomListPage.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 */; };
A9D99B9A2A1F7C3200DE27D3 /* RESTTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FAE67D28F83CA50033DD93 /* RESTTransport.swift */; };
A9DF789B2B7D1DF10094E4AD /* mullvad-api.h in Headers */ = {isa = PBXBuildFile; fileRef = 01EF6F2D2B6A51B100125696 /* mullvad-api.h */; settings = {ATTRIBUTES = (Private, ); }; };
A9DF789D2B7D1E8B0094E4AD /* LoggedInWithTimeUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 859089692B61763B003AF5F5 /* LoggedInWithTimeUITestCase.swift */; };
@@ -1195,19 +1210,26 @@
remoteGlobalIDString = 5840231E2A406BF5007B27AC;
remoteInfo = TunnelObfuscation;
};
- A9EC20F12A5D79ED0040D56E /* PBXContainerItemProxy */ = {
+ A944F2602B8DEFDB00473F4C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 58CE5E58224146200008646E /* Project object */;
proxyType = 1;
- remoteGlobalIDString = 5840231E2A406BF5007B27AC;
- remoteInfo = TunnelObfuscation;
+ remoteGlobalIDString = A944F25B2B8DEFDB00473F4C;
+ remoteInfo = MullvadPostQuantum;
};
- F04F959E2B21D02700431E08 /* PBXContainerItemProxy */ = {
+ A9630E412B8E10F700A65999 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 58CE5E58224146200008646E /* Project object */;
proxyType = 1;
- remoteGlobalIDString = 06799ABB28F98E1D00ACD94E;
- remoteInfo = MullvadREST;
+ remoteGlobalIDString = 58D223D4294C8E5E0029F5F8;
+ remoteInfo = MullvadTypes;
+ };
+ A9EC20F12A5D79ED0040D56E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 58CE5E58224146200008646E /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 5840231E2A406BF5007B27AC;
+ remoteInfo = TunnelObfuscation;
};
F0ACE30E2BE4E478006D5333 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
@@ -1228,6 +1250,7 @@
F0ACE3112BE4E478006D5333 /* MullvadMockData.framework in Embed Frameworks */,
58D223E7294C8F120029F5F8 /* MullvadTypes.framework in Embed Frameworks */,
58D223FA294C8FF10029F5F8 /* MullvadLogging.framework in Embed Frameworks */,
+ A944F2632B8DEFDB00473F4C /* MullvadPostQuantum.framework in Embed Frameworks */,
58B2FDDA2AA71D2A003EB5C6 /* MullvadSettings.framework in Embed Frameworks */,
A9EC20F02A5D79ED0040D56E /* TunnelObfuscation.framework in Embed Frameworks */,
7ABCA5B42A9349F20044A708 /* Routing.framework in Embed Frameworks */,
@@ -1309,6 +1332,27 @@
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
+ A906F94B2BA1E09A002BF22E /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ A9259FD52B8E06E90032C82B /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ A9259FD22B8E06E90032C82B /* MullvadPostQuantum.framework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F0ACE3292BE4E712006D5333 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@@ -1371,10 +1415,13 @@
449EB9FE2B95FF2500DFA4EB /* AccountMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountMock.swift; sourceTree = "<group>"; };
449EBA252B975B9700DFA4EB /* PostQuantumKeyReceiving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostQuantumKeyReceiving.swift; sourceTree = "<group>"; };
44B02E3A2BC5732D008EDF34 /* LoggingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggingTests.swift; sourceTree = "<group>"; };
+ 44BB5F962BE527F4002520EB /* TunnelState+UI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelState+UI.swift"; sourceTree = "<group>"; };
+ 44BB5F992BE529FE002520EB /* TunnelStateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelStateTests.swift; sourceTree = "<group>"; };
44DD7D232B6CFFD70005F67F /* StartTunnelOperationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartTunnelOperationTests.swift; sourceTree = "<group>"; };
44DD7D262B6D18FB0005F67F /* MockTunnelInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTunnelInteractor.swift; sourceTree = "<group>"; };
44DD7D282B7113CA0005F67F /* MockTunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTunnel.swift; sourceTree = "<group>"; };
44DD7D2C2B74E44A0005F67F /* QuantumResistanceSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuantumResistanceSettings.swift; sourceTree = "<group>"; };
+ 44DF8AC32BF20BD200869CA4 /* PacketTunnelActor+PostQuantum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PacketTunnelActor+PostQuantum.swift"; sourceTree = "<group>"; };
5802EBC42A8E44AC00E5CE4C /* AppRoutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRoutes.swift; sourceTree = "<group>"; };
5802EBC62A8E457A00E5CE4C /* AppRouteProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouteProtocol.swift; sourceTree = "<group>"; };
5802EBC82A8E45BA00E5CE4C /* ApplicationRouterDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationRouterDelegate.swift; sourceTree = "<group>"; };
@@ -1998,9 +2045,14 @@
A932D9F42B5EBB9D00999395 /* RESTTransportStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RESTTransportStub.swift; sourceTree = "<group>"; };
A935594B2B4C2DA900D5D524 /* APIAvailabilityTestRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIAvailabilityTestRequest.swift; sourceTree = "<group>"; };
A935594D2B4E919F00D5D524 /* Api.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Api.xcconfig; sourceTree = "<group>"; };
+ A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MullvadPostQuantum.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ A944F25E2B8DEFDB00473F4C /* MullvadPostQuantum.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MullvadPostQuantum.h; sourceTree = "<group>"; };
+ A944F2712B8E02E800473F4C /* libtalpid_tunnel_config_client.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtalpid_tunnel_config_client.a; path = "../target/aarch64-apple-ios/debug/libtalpid_tunnel_config_client.a"; sourceTree = "<group>"; };
A9467E7E2A29DEFE000DC21F /* RelayCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayCacheTests.swift; sourceTree = "<group>"; };
+ A948809A2BC9308D0090A44C /* PostQuantumKeyExchangeActor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostQuantumKeyExchangeActor.swift; sourceTree = "<group>"; };
A95EEE352B722CD600A8A39B /* TunnelMonitorState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelMonitorState.swift; sourceTree = "<group>"; };
A95EEE372B722DFC00A8A39B /* PingStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PingStats.swift; sourceTree = "<group>"; };
+ A9630E3B2B8E0E7B00A65999 /* talpid_tunnel_config_client.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = talpid_tunnel_config_client.h; path = "talpid-tunnel-config-client/include/talpid_tunnel_config_client.h"; sourceTree = "<group>"; };
A970C89C2B29E38C000A7684 /* Socks5UsernamePasswordCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Socks5UsernamePasswordCommand.swift; sourceTree = "<group>"; };
A97D25AD2B0BB18100946B2D /* ProtocolObfuscator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolObfuscator.swift; sourceTree = "<group>"; };
A97D25AF2B0BB5C400946B2D /* ProtocolObfuscationStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolObfuscationStub.swift; sourceTree = "<group>"; };
@@ -2016,6 +2068,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>"; };
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>"; };
@@ -2030,6 +2083,7 @@
A9E0317B2ACBFC7E0095D843 /* TunnelStore+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelStore+Stubs.swift"; sourceTree = "<group>"; };
A9E0317D2ACC32920095D843 /* TunnelStatusBlockObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelStatusBlockObserver.swift; sourceTree = "<group>"; };
A9E034632ABB302000E59A5A /* UIEdgeInsets+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIEdgeInsets+Extensions.swift"; sourceTree = "<group>"; };
+ A9EB4F9C2B7FAB21002A2D7A /* PostQuantumKeyNegotiator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostQuantumKeyNegotiator.swift; sourceTree = "<group>"; };
A9EC20E72A5D3A8C0040D56E /* CoordinatesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoordinatesTests.swift; sourceTree = "<group>"; };
A9F360332AAB626300F53531 /* VPNConnectionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNConnectionProtocol.swift; sourceTree = "<group>"; };
E1187ABA289BBB850024E748 /* OutOfTimeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutOfTimeViewController.swift; sourceTree = "<group>"; };
@@ -2196,6 +2250,7 @@
58F0974E2A20C31100DA2DAD /* WireGuardKitTypes in Frameworks */,
58C7A4492A863F490060C66F /* PacketTunnelCore.framework in Frameworks */,
58D223F9294C8FF00029F5F8 /* MullvadLogging.framework in Frameworks */,
+ A944F2622B8DEFDB00473F4C /* MullvadPostQuantum.framework in Frameworks */,
58D223E6294C8F120029F5F8 /* MullvadTypes.framework in Frameworks */,
7ABCA5B32A9349F20044A708 /* Routing.framework in Frameworks */,
58D223CC294C8BCB0029F5F8 /* Operations.framework in Frameworks */,
@@ -2209,6 +2264,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ A9630E442B8E115A00A65999 /* MullvadPostQuantum.framework in Frameworks */,
589C6A7D2A45B06800DAD3EF /* TunnelObfuscation.framework in Frameworks */,
58FE25C62AA72779003D1918 /* PacketTunnelCore.framework in Frameworks */,
58FE25CE2AA72802003D1918 /* MullvadSettings.framework in Frameworks */,
@@ -2284,6 +2340,16 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ A944F2592B8DEFDB00473F4C /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ A9D7E43C2BFCE43200213D55 /* WireGuardKitTypes in Frameworks */,
+ A9630E432B8E10FB00A65999 /* MullvadTypes.framework in Frameworks */,
+ A944F2722B8E02F600473F4C /* libtalpid_tunnel_config_client.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F0ACE3052BE4E478006D5333 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -2468,6 +2534,7 @@
44DD7D262B6D18FB0005F67F /* MockTunnelInteractor.swift */,
44DD7D232B6CFFD70005F67F /* StartTunnelOperationTests.swift */,
A9A5F9A12ACB003D0083449F /* TunnelManagerTests.swift */,
+ 44BB5F992BE529FE002520EB /* TunnelStateTests.swift */,
A9E0317B2ACBFC7E0095D843 /* TunnelStore+Stubs.swift */,
A9E031792ACB0AE70095D843 /* UIApplication+Stubs.swift */,
58165EBD2A262CBB00688EAD /* WgKeyRotationTests.swift */,
@@ -2582,6 +2649,7 @@
5820676326E771DB00655B05 /* TunnelManagerErrors.swift */,
5823FA5326CE49F600283BF8 /* TunnelObserver.swift */,
58B93A1226C3F13600A55733 /* TunnelState.swift */,
+ 44BB5F962BE527F4002520EB /* TunnelState+UI.swift */,
5803B4B12940A48700C23744 /* TunnelStore.swift */,
5842102F282D8A3C00F24E46 /* UpdateAccountDataOperation.swift */,
58421031282E42B000F24E46 /* UpdateDeviceDataOperation.swift */,
@@ -2982,6 +3050,7 @@
584F991F2902CBDD001F858D /* Frameworks */ = {
isa = PBXGroup;
children = (
+ A944F2712B8E02E800473F4C /* libtalpid_tunnel_config_client.a */,
01EF6F332B6A590700125696 /* libmullvad_api.a */,
01EF6F312B6A58F000125696 /* debug */,
01EF6F2F2B6A588300125696 /* aarch64-apple-ios */,
@@ -3046,6 +3115,7 @@
58342C032AAB61FB003BA12D /* State+Extensions.swift */,
586E8DB72AAF4AC4007BF3DA /* Task+Duration.swift */,
58DDA18E2ABC32380039C360 /* Timings.swift */,
+ 44DF8AC32BF20BD200869CA4 /* PacketTunnelActor+PostQuantum.swift */,
);
path = Actor;
sourceTree = "<group>";
@@ -3367,7 +3437,6 @@
children = (
58F3C0A824A50C0E003E76BE /* Assets */,
58ECD29023F178FD004298B6 /* Configurations */,
- 584F991F2902CBDD001F858D /* Frameworks */,
01EF6F2D2B6A51B100125696 /* mullvad-api.h */,
8556EB512B9A1C6900D26DD4 /* MullvadApi.swift */,
58D223F4294C8FF00029F5F8 /* MullvadLogging */,
@@ -3385,13 +3454,15 @@
58CE5E7A224146470008646E /* PacketTunnel */,
58C7A4372A863F450060C66F /* PacketTunnelCore */,
58C7A4432A863F490060C66F /* PacketTunnelCoreTests */,
- 58CE5E61224146200008646E /* Products */,
7A88DCCF2A8FABBE00D2FF0E /* Routing */,
7A88DCDD2A8FABBE00D2FF0E /* RoutingTests */,
589A454A28DDF59B00565204 /* Shared */,
7A83C3FC2A55B39500DFB83A /* TestPlans */,
584023202A406BF5007B27AC /* TunnelObfuscation */,
58695A9E2A4ADA9200328DB3 /* TunnelObfuscationTests */,
+ A944F25D2B8DEFDB00473F4C /* MullvadPostQuantum */,
+ 58CE5E61224146200008646E /* Products */,
+ 584F991F2902CBDD001F858D /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -3417,6 +3488,7 @@
58B2FDD32AA71D2A003EB5C6 /* MullvadSettings.framework */,
852969252B4D9C1F007EAD4C /* MullvadVPNUITests.xctest */,
F0ACE3082BE4E478006D5333 /* MullvadMockData.framework */,
+ A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */,
);
name = Products;
sourceTree = "<group>";
@@ -3459,6 +3531,7 @@
58F3F3682AA08E2200D3B0A4 /* PacketTunnelProvider */,
58915D662A25F9F20066445B /* DeviceCheck */,
588395612A9DF497008B63F6 /* WireGuardAdapter */,
+ A948809A2BC9308D0090A44C /* PostQuantumKeyExchangeActor.swift */,
);
path = PacketTunnel;
sourceTree = "<group>";
@@ -3910,6 +3983,17 @@
path = Socks5;
sourceTree = "<group>";
};
+ A944F25D2B8DEFDB00473F4C /* MullvadPostQuantum */ = {
+ isa = PBXGroup;
+ children = (
+ A944F25E2B8DEFDB00473F4C /* MullvadPostQuantum.h */,
+ A9A557F42B7E3E5C0017ADA8 /* PacketTunnelProvider+TCPConnection.swift */,
+ A9EB4F9C2B7FAB21002A2D7A /* PostQuantumKeyNegotiator.swift */,
+ A9630E3B2B8E0E7B00A65999 /* talpid_tunnel_config_client.h */,
+ );
+ path = MullvadPostQuantum;
+ sourceTree = "<group>";
+ };
F028A5472A336E1900C0CAA3 /* RedeemVoucher */ = {
isa = PBXGroup;
children = (
@@ -4186,6 +4270,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ A944F2572B8DEFDB00473F4C /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ A944F25F2B8DEFDB00473F4C /* MullvadPostQuantum.h in Headers */,
+ A9630E3C2B8E0E7B00A65999 /* talpid_tunnel_config_client.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F0ACE3032BE4E478006D5333 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
@@ -4255,7 +4348,6 @@
buildRules = (
);
dependencies = (
- F04F959F2B21D02700431E08 /* PBXTargetDependency */,
A91614D32B108F4D00F416EB /* PBXTargetDependency */,
);
name = TunnelObfuscation;
@@ -4413,6 +4505,7 @@
7ABCA5B62A9349F20044A708 /* PBXTargetDependency */,
58B2FDD82AA71D2A003EB5C6 /* PBXTargetDependency */,
F0ACE30F2BE4E478006D5333 /* PBXTargetDependency */,
+ A944F2612B8DEFDB00473F4C /* PBXTargetDependency */,
);
name = MullvadVPN;
packageProductDependencies = (
@@ -4430,6 +4523,7 @@
58CE5E75224146470008646E /* Sources */,
58CE5E76224146470008646E /* Frameworks */,
58CE5E77224146470008646E /* Resources */,
+ A9259FD52B8E06E90032C82B /* Embed Frameworks */,
);
buildRules = (
);
@@ -4612,6 +4706,30 @@
productReference = 852969252B4D9C1F007EAD4C /* MullvadVPNUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
+ A944F25B2B8DEFDB00473F4C /* MullvadPostQuantum */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = A944F2682B8DEFDB00473F4C /* Build configuration list for PBXNativeTarget "MullvadPostQuantum" */;
+ buildPhases = (
+ A944F2692B8DF00C00473F4C /* Build Talpid Tunnel Config Client */,
+ A944F2572B8DEFDB00473F4C /* Headers */,
+ A944F2582B8DEFDB00473F4C /* Sources */,
+ A944F2592B8DEFDB00473F4C /* Frameworks */,
+ A944F25A2B8DEFDB00473F4C /* Resources */,
+ A906F94B2BA1E09A002BF22E /* Embed Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ A9630E422B8E10F700A65999 /* PBXTargetDependency */,
+ );
+ name = MullvadPostQuantum;
+ packageProductDependencies = (
+ A906F9492BA1E09A002BF22E /* WireGuardKitTypes */,
+ );
+ productName = MullvadPostQuantum;
+ productReference = A944F25C2B8DEFDB00473F4C /* MullvadPostQuantum.framework */;
+ productType = "com.apple.product-type.framework";
+ };
F0ACE3072BE4E478006D5333 /* MullvadMockData */ = {
isa = PBXNativeTarget;
buildConfigurationList = F0ACE3162BE4E479006D5333 /* Build configuration list for PBXNativeTarget "MullvadMockData" */;
@@ -4720,8 +4838,11 @@
CreatedOnToolsVersion = 15.1;
TestTargetID = 58CE5E5F224146200008646E;
};
+ A944F25B2B8DEFDB00473F4C = {
+ CreatedOnToolsVersion = 15.2;
+ };
F0ACE3072BE4E478006D5333 = {
- CreatedOnToolsVersion = 15.3;
+ CreatedOnToolsVersion = 15.2;
};
};
};
@@ -4762,6 +4883,7 @@
7A88DCCD2A8FABBE00D2FF0E /* Routing */,
7A88DCD62A8FABBE00D2FF0E /* RoutingTests */,
F0ACE3072BE4E478006D5333 /* MullvadMockData */,
+ A944F25B2B8DEFDB00473F4C /* MullvadPostQuantum */,
);
};
/* End PBXProject section */
@@ -4893,6 +5015,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ A944F25A2B8DEFDB00473F4C /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F0ACE3062BE4E478006D5333 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -4977,6 +5106,25 @@
shellPath = /bin/sh;
shellScript = "CARGO_TARGET_DIR=${PROJECT_DIR}/../target bash ${PROJECT_DIR}/build-rust-library.sh tunnel-obfuscator-proxy\n";
};
+ A944F2692B8DF00C00473F4C /* Build Talpid Tunnel Config Client */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ name = "Build Talpid Tunnel Config Client";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "CARGO_TARGET_DIR=${PROJECT_DIR}/../target bash ${PROJECT_DIR}/build-rust-library.sh talpid-tunnel-config-client\n";
+ };
F05F39962B21C704006E60A7 /* Prebuild relays */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -5247,6 +5395,7 @@
7A9BE5AB2B909A1700E2A7D0 /* LocationDataSourceProtocol.swift in Sources */,
A9A5FA2A2ACB05160083449F /* CoordinatesTests.swift in Sources */,
44DD7D242B6CFFD70005F67F /* StartTunnelOperationTests.swift in Sources */,
+ 44BB5F982BE527F4002520EB /* TunnelState+UI.swift in Sources */,
A9A5FA2B2ACB05160083449F /* CustomDateComponentsFormattingTests.swift in Sources */,
A9A5FA2C2ACB05160083449F /* DeviceCheckOperationTests.swift in Sources */,
A9A5FA2D2ACB05160083449F /* DurationTests.swift in Sources */,
@@ -5326,6 +5475,7 @@
58FE25DA2AA72A8F003D1918 /* PacketTunnelActor.swift in Sources */,
587A5E522ADD7569003A70F1 /* ObservedState+Extensions.swift in Sources */,
58FE25E62AA738E8003D1918 /* TunnelAdapterProtocol.swift in Sources */,
+ 44DF8AC42BF20BD200869CA4 /* PacketTunnelActor+PostQuantum.swift in Sources */,
583832252AC318A100EA2071 /* PacketTunnelActor+ConnectionMonitoring.swift in Sources */,
58C7A4552A863FB90060C66F /* TunnelMonitor.swift in Sources */,
58C7AF182ABD84AB007EDD7A /* ProxyURLResponse.swift in Sources */,
@@ -5424,6 +5574,7 @@
7A9CCCB72A96302800DD6A34 /* RevokedCoordinator.swift in Sources */,
7A6389F82B864CDF008E77E1 /* LocationNode.swift in Sources */,
587D96742886D87C00CD8F1C /* DeviceManagementContentView.swift in Sources */,
+ 44BB5F972BE527F4002520EB /* TunnelState+UI.swift in Sources */,
7A11DD0B2A9495D400098CD8 /* AppRoutes.swift in Sources */,
5827B0902B0CAA0500CCBBA1 /* EditAccessMethodCoordinator.swift in Sources */,
5846227126E229F20035F7C2 /* StoreSubscription.swift in Sources */,
@@ -5776,6 +5927,7 @@
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 */,
@@ -5964,6 +6116,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ A944F2582B8DEFDB00473F4C /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ A9630E492B921E6D00A65999 /* PacketTunnelProvider+TCPConnection.swift in Sources */,
+ A944F26A2B8DF32900473F4C /* PostQuantumKeyNegotiator.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F0ACE3042BE4E478006D5333 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -6179,16 +6340,21 @@
target = 5840231E2A406BF5007B27AC /* TunnelObfuscation */;
targetProxy = A91D78E12B03BDE500FCD5D3 /* PBXContainerItemProxy */;
};
+ A944F2612B8DEFDB00473F4C /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = A944F25B2B8DEFDB00473F4C /* MullvadPostQuantum */;
+ targetProxy = A944F2602B8DEFDB00473F4C /* PBXContainerItemProxy */;
+ };
+ A9630E422B8E10F700A65999 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 58D223D4294C8E5E0029F5F8 /* MullvadTypes */;
+ targetProxy = A9630E412B8E10F700A65999 /* PBXContainerItemProxy */;
+ };
A9EC20F22A5D79ED0040D56E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5840231E2A406BF5007B27AC /* TunnelObfuscation */;
targetProxy = A9EC20F12A5D79ED0040D56E /* PBXContainerItemProxy */;
};
- F04F959F2B21D02700431E08 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = 06799ABB28F98E1D00ACD94E /* MullvadREST */;
- targetProxy = F04F959E2B21D02700431E08 /* PBXContainerItemProxy */;
- };
F0ACE30F2BE4E478006D5333 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F0ACE3072BE4E478006D5333 /* MullvadMockData */;
@@ -6804,6 +6970,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_STRICT_CONCURRENCY = minimal;
SWIFT_VERSION = 5.0;
};
name = Debug;
@@ -6824,6 +6991,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER)";
PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_STRICT_CONCURRENCY = minimal;
SWIFT_VERSION = 5.0;
};
name = Release;
@@ -6844,6 +7012,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnel";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_STRICT_CONCURRENCY = minimal;
SWIFT_VERSION = 5.0;
};
name = Debug;
@@ -6863,6 +7032,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnel";
PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_STRICT_CONCURRENCY = minimal;
SWIFT_VERSION = 5.0;
};
name = Release;
@@ -7473,6 +7643,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Mullvad VPN Development";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_STRICT_CONCURRENCY = minimal;
SWIFT_VERSION = 5.0;
};
name = Staging;
@@ -7511,6 +7682,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Packet Tunnel Development";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_STRICT_CONCURRENCY = minimal;
SWIFT_VERSION = 5.0;
};
name = Staging;
@@ -8010,6 +8182,211 @@
};
name = MockRelease;
};
+ A944F2642B8DEFDB00473F4C /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 5808273928487E3E006B77A4 /* Base.xcconfig */;
+ buildSettings = {
+ APPLICATION_IDENTIFIER = net.mullvad.mullvadVPN;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Manual;
+ CURRENT_PROJECT_VERSION = 1;
+ DEFINES_MODULE = YES;
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 4;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ ENABLE_MODULE_VERIFIER = NO;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Mullvad VPN AB. All rights reserved.";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 14.2;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/debug";
+ "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/debug";
+ "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = "$(PROJECT_DIR)/../target/x86_64-apple-ios/debug";
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ MODULEMAP_PRIVATE_FILE = $PROJECT_DIR/MullvadPostQuantum/module.private.modulemap;
+ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
+ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
+ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadPostQuantum";
+ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SKIP_INSTALL = YES;
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Debug;
+ };
+ A944F2652B8DEFDB00473F4C /* Staging */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 5808273928487E3E006B77A4 /* Base.xcconfig */;
+ buildSettings = {
+ APPLICATION_IDENTIFIER = net.mullvad.mullvadVPN;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Manual;
+ CURRENT_PROJECT_VERSION = 1;
+ DEFINES_MODULE = YES;
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 4;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ ENABLE_MODULE_VERIFIER = NO;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Mullvad VPN AB. All rights reserved.";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 14.2;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ LIBRARY_SEARCH_PATHS = "";
+ "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/debug";
+ "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/debug";
+ "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = "$(PROJECT_DIR)/../target/x86_64-apple-ios/debug";
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ MODULEMAP_PRIVATE_FILE = $PROJECT_DIR/MullvadPostQuantum/module.private.modulemap;
+ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
+ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
+ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadPostQuantum";
+ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SKIP_INSTALL = YES;
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Staging;
+ };
+ A944F2662B8DEFDB00473F4C /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 5808273928487E3E006B77A4 /* Base.xcconfig */;
+ buildSettings = {
+ APPLICATION_IDENTIFIER = net.mullvad.mullvadVPN;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Distribution";
+ CODE_SIGN_STYLE = Manual;
+ CURRENT_PROJECT_VERSION = 1;
+ DEFINES_MODULE = YES;
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 4;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ ENABLE_MODULE_VERIFIER = NO;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Mullvad VPN AB. All rights reserved.";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 14.2;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ LIBRARY_SEARCH_PATHS = "";
+ "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/release";
+ "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/release";
+ "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = "$(PROJECT_DIR)/../target/x86_64-apple-ios/release";
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ MODULEMAP_PRIVATE_FILE = $PROJECT_DIR/MullvadPostQuantum/module.private.modulemap;
+ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
+ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
+ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadPostQuantum";
+ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SKIP_INSTALL = YES;
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Release;
+ };
+ A944F2672B8DEFDB00473F4C /* MockRelease */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 5808273928487E3E006B77A4 /* Base.xcconfig */;
+ buildSettings = {
+ APPLICATION_IDENTIFIER = net.mullvad.mullvadVPN;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Manual;
+ CURRENT_PROJECT_VERSION = 1;
+ DEFINES_MODULE = YES;
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = CKG9MXH72F;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 4;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ ENABLE_MODULE_VERIFIER = NO;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Mullvad VPN AB. All rights reserved.";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 14.2;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ LIBRARY_SEARCH_PATHS = "";
+ "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/release";
+ "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/release";
+ "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = "$(PROJECT_DIR)/../target/x86_64-apple-ios/release";
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ MODULEMAP_PRIVATE_FILE = $PROJECT_DIR/MullvadPostQuantum/module.private.modulemap;
+ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
+ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
+ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadPostQuantum";
+ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SKIP_INSTALL = YES;
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = MockRelease;
+ };
A9E99CE12B5195E600869AF2 /* MockRelease */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -8085,6 +8462,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER)";
PRODUCT_NAME = "$(TARGET_NAME)";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Mullvad VPN Development";
+ SWIFT_STRICT_CONCURRENCY = minimal;
SWIFT_VERSION = 5.0;
};
name = MockRelease;
@@ -8119,6 +8497,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnel";
PRODUCT_NAME = "$(TARGET_NAME)";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Packet Tunnel Development";
+ SWIFT_STRICT_CONCURRENCY = minimal;
SWIFT_VERSION = 5.0;
};
name = MockRelease;
@@ -8673,6 +9052,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "Apple Development";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
@@ -8721,7 +9101,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "Apple Development";
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Distribution";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
@@ -8987,6 +9367,17 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ A944F2682B8DEFDB00473F4C /* Build configuration list for PBXNativeTarget "MullvadPostQuantum" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ A944F2642B8DEFDB00473F4C /* Debug */,
+ A944F2652B8DEFDB00473F4C /* Staging */,
+ A944F2662B8DEFDB00473F4C /* Release */,
+ A944F2672B8DEFDB00473F4C /* MockRelease */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
F0ACE3162BE4E479006D5333 /* Build configuration list for PBXNativeTarget "MullvadMockData" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -9070,6 +9461,11 @@
package = 58F097482A20C30000DA2DAD /* XCRemoteSwiftPackageReference "wireguard-apple" */;
productName = WireGuardKitTypes;
};
+ A906F9492BA1E09A002BF22E /* 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/MullvadPostQuantum.xcscheme b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadPostQuantum.xcscheme
new file mode 100644
index 0000000000..0d18c5e1d9
--- /dev/null
+++ b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadPostQuantum.xcscheme
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1520"
+ version = "1.7">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "A944F25B2B8DEFDB00473F4C"
+ BuildableName = "MullvadPostQuantum.framework"
+ BlueprintName = "MullvadPostQuantum"
+ ReferencedContainer = "container:MullvadVPN.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ shouldAutocreateTestPlan = "YES">
+ </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">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "A944F25B2B8DEFDB00473F4C"
+ BuildableName = "MullvadPostQuantum.framework"
+ BlueprintName = "MullvadPostQuantum"
+ ReferencedContainer = "container:MullvadVPN.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift b/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift
index 0c2b8678f8..80352cdda2 100644
--- a/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift
+++ b/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift
@@ -980,14 +980,10 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo
guard tunnelManager.deviceState.isLoggedIn else { return false }
switch tunnelManager.tunnelStatus.state {
- case .connected, .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .error:
+ case .connected, .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .error,
+ .negotiatingPostQuantumKey:
tunnelManager.reconnectTunnel(selectNewRelay: true)
- #if DEBUG
- case .negotiatingKey:
- tunnelManager.reconnectTunnel(selectNewRelay: true)
- #endif
-
case .disconnecting, .disconnected:
tunnelManager.startTunnel()
diff --git a/ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelProviderHost.swift b/ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelProviderHost.swift
index e7bf690f87..ea5260b8af 100644
--- a/ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelProviderHost.swift
+++ b/ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelProviderHost.swift
@@ -176,14 +176,16 @@ final class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate {
guard let selectedRelay = selectedRelay else { return }
do {
+ let settings = try SettingsManager.readSettings()
observedState = .connected(
ObservedConnectionState(
selectedRelay: selectedRelay,
- relayConstraints: try SettingsManager.readSettings().relayConstraints,
+ relayConstraints: settings.relayConstraints,
networkReachability: .reachable,
connectionAttemptCount: 0,
transportLayer: .udp,
- remotePort: selectedRelay.endpoint.ipv4Relay.port
+ remotePort: selectedRelay.endpoint.ipv4Relay.port,
+ isPostQuantum: settings.tunnelQuantumResistance.isEnabled
)
)
} catch {
diff --git a/ios/MullvadVPN/TunnelManager/MapConnectionStatusOperation.swift b/ios/MullvadVPN/TunnelManager/MapConnectionStatusOperation.swift
index e2a87e882a..4957af7c52 100644
--- a/ios/MullvadVPN/TunnelManager/MapConnectionStatusOperation.swift
+++ b/ios/MullvadVPN/TunnelManager/MapConnectionStatusOperation.swift
@@ -51,21 +51,19 @@ class MapConnectionStatusOperation: AsyncOperation {
switch observedState {
case let .connected(connectionState):
return connectionState.isNetworkReachable
- ? .connected(connectionState.selectedRelay)
+ ? .connected(connectionState.selectedRelay, isPostQuantum: connectionState.isPostQuantum)
: .waitingForConnectivity(.noConnection)
case let .connecting(connectionState):
return connectionState.isNetworkReachable
- ? .connecting(connectionState.selectedRelay)
+ ? .connecting(connectionState.selectedRelay, isPostQuantum: connectionState.isPostQuantum)
: .waitingForConnectivity(.noConnection)
- #if DEBUG
- case let .negotiatingKey(connectionState):
+ case let .negotiatingPostQuantumKey(connectionState, privateKey):
return connectionState.isNetworkReachable
- ? .negotiatingKey(connectionState.selectedRelay)
+ ? .negotiatingPostQuantumKey(connectionState.selectedRelay, privateKey)
: .waitingForConnectivity(.noConnection)
- #endif
case let .reconnecting(connectionState):
return connectionState.isNetworkReachable
- ? .reconnecting(connectionState.selectedRelay)
+ ? .reconnecting(connectionState.selectedRelay, isPostQuantum: connectionState.isPostQuantum)
: .waitingForConnectivity(.noConnection)
case let .error(blockedState):
return .error(blockedState.reason)
diff --git a/ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift b/ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift
index 9474a0a481..cd9e8b7a88 100644
--- a/ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift
+++ b/ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift
@@ -90,7 +90,10 @@ class StartTunnelOperation: ResultOperation<Void> {
interactor.updateTunnelStatus { tunnelStatus in
tunnelStatus = TunnelStatus()
- tunnelStatus.state = .connecting(selectedRelay)
+ tunnelStatus.state = .connecting(
+ selectedRelay,
+ isPostQuantum: interactor.settings.tunnelQuantumResistance.isEnabled
+ )
}
try tunnel.start(options: tunnelOptions.rawOptions())
diff --git a/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift b/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift
index 4701a4238c..1bd7a4f9c7 100644
--- a/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift
+++ b/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift
@@ -35,14 +35,10 @@ class StopTunnelOperation: ResultOperation<Void> {
finish(result: .success(()))
- case .connected, .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .error:
+ case .connected, .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .error,
+ .negotiatingPostQuantumKey:
doShutDownTunnel()
- #if DEBUG
- case .negotiatingKey:
- doShutDownTunnel()
- #endif
-
case .disconnected, .disconnecting, .pendingReconnect, .waitingForConnectivity(.noNetwork):
finish(result: .success(()))
}
diff --git a/ios/MullvadVPN/TunnelManager/TunnelManager.swift b/ios/MullvadVPN/TunnelManager/TunnelManager.swift
index 8f3b69112c..f162810199 100644
--- a/ios/MullvadVPN/TunnelManager/TunnelManager.swift
+++ b/ios/MullvadVPN/TunnelManager/TunnelManager.swift
@@ -670,16 +670,11 @@ final class TunnelManager: StorePaymentObserver {
refreshDeviceState()
}
switch newTunnelStatus.state {
- case .connecting, .reconnecting:
+ case .connecting, .reconnecting, .negotiatingPostQuantumKey:
// Start polling tunnel status to keep the relay information up to date
// while the tunnel process is trying to connect.
startPollingTunnelStatus(interval: establishingTunnelStatusPollInterval)
- #if DEBUG
- case .negotiatingKey:
- startPollingTunnelStatus(interval: establishingTunnelStatusPollInterval)
- #endif
-
case .connected, .waitingForConnectivity(.noConnection):
// Start polling tunnel status to keep connectivity status up to date.
startPollingTunnelStatus(interval: establishedTunnelStatusPollInterval)
diff --git a/ios/MullvadVPN/TunnelManager/TunnelState+UI.swift b/ios/MullvadVPN/TunnelManager/TunnelState+UI.swift
new file mode 100644
index 0000000000..eefb1db415
--- /dev/null
+++ b/ios/MullvadVPN/TunnelManager/TunnelState+UI.swift
@@ -0,0 +1,259 @@
+//
+// TunnelState+UI.swift
+// MullvadVPN
+//
+// Created by Andrew Bulhak on 2024-05-03.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import UIKit
+
+extension TunnelState {
+ var textColorForSecureLabel: UIColor {
+ switch self {
+ case .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .negotiatingPostQuantumKey:
+ .white
+
+ case .connected:
+ .successColor
+
+ case .disconnecting, .disconnected, .pendingReconnect, .waitingForConnectivity(.noNetwork), .error:
+ .dangerColor
+ }
+ }
+
+ var shouldEnableButtons: Bool {
+ if case .waitingForConnectivity(.noNetwork) = self {
+ return false
+ }
+
+ return true
+ }
+
+ var localizedTitleForSecureLabel: String {
+ switch self {
+ case let .connecting(_, isPostQuantum), let .reconnecting(_, isPostQuantum):
+ if isPostQuantum {
+ NSLocalizedString(
+ "TUNNEL_STATE_PQ_CONNECTING",
+ tableName: "Main",
+ value: "Creating quantum secure connection",
+ comment: ""
+ )
+ } else {
+ NSLocalizedString(
+ "TUNNEL_STATE_CONNECTING",
+ tableName: "Main",
+ value: "Creating secure connection",
+ comment: ""
+ )
+ }
+
+ case .negotiatingPostQuantumKey:
+ NSLocalizedString(
+ "TUNNEL_STATE_NEGOTIATING_KEY",
+ tableName: "Main",
+ value: "Creating quantum secure connection",
+ comment: ""
+ )
+
+ case let .connected(_, isPostQuantum):
+ if isPostQuantum {
+ NSLocalizedString(
+ "TUNNEL_STATE_PQ_CONNECTED",
+ tableName: "Main",
+ value: "Quantum secure connection",
+ comment: ""
+ )
+ } else {
+ NSLocalizedString(
+ "TUNNEL_STATE_CONNECTED",
+ tableName: "Main",
+ value: "Secure connection",
+ comment: ""
+ )
+ }
+
+ case .disconnecting(.nothing):
+ NSLocalizedString(
+ "TUNNEL_STATE_DISCONNECTING",
+ tableName: "Main",
+ value: "Disconnecting",
+ comment: ""
+ )
+ case .disconnecting(.reconnect), .pendingReconnect:
+ NSLocalizedString(
+ "TUNNEL_STATE_PENDING_RECONNECT",
+ tableName: "Main",
+ value: "Reconnecting",
+ comment: ""
+ )
+
+ case .disconnected:
+ NSLocalizedString(
+ "TUNNEL_STATE_DISCONNECTED",
+ tableName: "Main",
+ value: "Unsecured connection",
+ comment: ""
+ )
+
+ case .waitingForConnectivity(.noConnection), .error:
+ NSLocalizedString(
+ "TUNNEL_STATE_WAITING_FOR_CONNECTIVITY",
+ tableName: "Main",
+ value: "Blocked connection",
+ comment: ""
+ )
+
+ case .waitingForConnectivity(.noNetwork):
+ NSLocalizedString(
+ "TUNNEL_STATE_NO_NETWORK",
+ tableName: "Main",
+ value: "No network",
+ comment: ""
+ )
+ }
+ }
+
+ var localizedTitleForSelectLocationButton: String? {
+ switch self {
+ case .disconnecting(.reconnect), .pendingReconnect:
+ NSLocalizedString(
+ "SWITCH_LOCATION_BUTTON_TITLE",
+ tableName: "Main",
+ value: "Select location",
+ comment: ""
+ )
+
+ case .disconnected, .disconnecting(.nothing):
+ NSLocalizedString(
+ "SELECT_LOCATION_BUTTON_TITLE",
+ tableName: "Main",
+ value: "Select location",
+ comment: ""
+ )
+
+ case .connecting, .connected, .reconnecting, .waitingForConnectivity, .error:
+ NSLocalizedString(
+ "SWITCH_LOCATION_BUTTON_TITLE",
+ tableName: "Main",
+ value: "Switch location",
+ comment: ""
+ )
+
+ case .negotiatingPostQuantumKey:
+ NSLocalizedString(
+ "SWITCH_LOCATION_BUTTON_TITLE",
+ tableName: "Main",
+ value: "Switch location",
+ comment: ""
+ )
+ }
+ }
+
+ var localizedAccessibilityLabel: String {
+ switch self {
+ case let .connecting(_, isPostQuantum):
+ if isPostQuantum {
+ NSLocalizedString(
+ "TUNNEL_STATE_PQ_CONNECTING_ACCESSIBILITY_LABEL",
+ tableName: "Main",
+ value: "Creating quantum secure connection",
+ comment: ""
+ )
+ } else {
+ NSLocalizedString(
+ "TUNNEL_STATE_CONNECTING_ACCESSIBILITY_LABEL",
+ tableName: "Main",
+ value: "Creating secure connection",
+ comment: ""
+ )
+ }
+
+ case .negotiatingPostQuantumKey:
+ NSLocalizedString(
+ "TUNNEL_STATE_CONNECTING_ACCESSIBILITY_LABEL",
+ tableName: "Main",
+ value: "Creating quantum secure connection",
+ comment: ""
+ )
+
+ case let .connected(tunnelInfo, isPostQuantum):
+ if isPostQuantum {
+ String(
+ format: NSLocalizedString(
+ "TUNNEL_STATE_PQ_CONNECTED_ACCESSIBILITY_LABEL",
+ tableName: "Main",
+ value: "Quantum secure connection. Connected to %@, %@",
+ comment: ""
+ ),
+ tunnelInfo.location.city,
+ tunnelInfo.location.country
+ )
+ } else {
+ String(
+ format: NSLocalizedString(
+ "TUNNEL_STATE_CONNECTED_ACCESSIBILITY_LABEL",
+ tableName: "Main",
+ value: "Secure connection. Connected to %@, %@",
+ comment: ""
+ ),
+ tunnelInfo.location.city,
+ tunnelInfo.location.country
+ )
+ }
+
+ case .disconnected:
+ NSLocalizedString(
+ "TUNNEL_STATE_DISCONNECTED_ACCESSIBILITY_LABEL",
+ tableName: "Main",
+ value: "Unsecured connection",
+ comment: ""
+ )
+
+ case let .reconnecting(tunnelInfo, _):
+ String(
+ format: NSLocalizedString(
+ "TUNNEL_STATE_RECONNECTING_ACCESSIBILITY_LABEL",
+ tableName: "Main",
+ value: "Reconnecting to %@, %@",
+ comment: ""
+ ),
+ tunnelInfo.location.city,
+ tunnelInfo.location.country
+ )
+
+ case .waitingForConnectivity(.noConnection), .error:
+ NSLocalizedString(
+ "TUNNEL_STATE_WAITING_FOR_CONNECTIVITY_ACCESSIBILITY_LABEL",
+ tableName: "Main",
+ value: "Blocked connection",
+ comment: ""
+ )
+
+ case .waitingForConnectivity(.noNetwork):
+ NSLocalizedString(
+ "TUNNEL_STATE_NO_NETWORK_ACCESSIBILITY_LABEL",
+ tableName: "Main",
+ value: "No network",
+ comment: ""
+ )
+
+ case .disconnecting(.nothing):
+ NSLocalizedString(
+ "TUNNEL_STATE_DISCONNECTING_ACCESSIBILITY_LABEL",
+ tableName: "Main",
+ value: "Disconnecting",
+ comment: ""
+ )
+
+ case .disconnecting(.reconnect), .pendingReconnect:
+ NSLocalizedString(
+ "TUNNEL_STATE_PENDING_RECONNECT_ACCESSIBILITY_LABEL",
+ tableName: "Main",
+ value: "Reconnecting",
+ comment: ""
+ )
+ }
+ }
+}
diff --git a/ios/MullvadVPN/TunnelManager/TunnelState.swift b/ios/MullvadVPN/TunnelManager/TunnelState.swift
index 08b889899e..76148bdbb8 100644
--- a/ios/MullvadVPN/TunnelManager/TunnelState.swift
+++ b/ios/MullvadVPN/TunnelManager/TunnelState.swift
@@ -9,6 +9,7 @@
import Foundation
import MullvadTypes
import PacketTunnelCore
+import WireGuardKitTypes
/// A struct describing the tunnel status.
struct TunnelStatus: Equatable, CustomStringConvertible {
@@ -48,15 +49,13 @@ enum TunnelState: Equatable, CustomStringConvertible {
case pendingReconnect
/// Connecting the tunnel.
- case connecting(SelectedRelay?)
+ case connecting(SelectedRelay?, isPostQuantum: Bool)
- #if DEBUG
/// Negotiating a key for post-quantum resistance
- case negotiatingKey(SelectedRelay)
- #endif
+ case negotiatingPostQuantumKey(SelectedRelay, PrivateKey)
/// Connected the tunnel
- case connected(SelectedRelay)
+ case connected(SelectedRelay, isPostQuantum: Bool)
/// Disconnecting the tunnel
case disconnecting(ActionAfterDisconnect)
@@ -69,7 +68,7 @@ enum TunnelState: Equatable, CustomStringConvertible {
/// 1. Asking the running tunnel to reconnect to new relay via IPC.
/// 2. Tunnel attempts to reconnect to new relay as the current relay appears to be
/// dysfunctional.
- case reconnecting(SelectedRelay)
+ case reconnecting(SelectedRelay, isPostQuantum: Bool)
/// Waiting for connectivity to come back up.
case waitingForConnectivity(WaitingForConnectionReason)
@@ -81,55 +80,45 @@ enum TunnelState: Equatable, CustomStringConvertible {
switch self {
case .pendingReconnect:
"pending reconnect after disconnect"
- case let .connecting(tunnelRelay):
+ case let .connecting(tunnelRelay, isPostQuantum):
if let tunnelRelay {
- "connecting to \(tunnelRelay.hostname)"
+ "connecting \(isPostQuantum ? "(PQ) " : "")to \(tunnelRelay.hostname)"
} else {
- "connecting, fetching relay"
+ "connecting\(isPostQuantum ? " (PQ)" : ""), fetching relay"
}
- case let .connected(tunnelRelay):
- "connected to \(tunnelRelay.hostname)"
+ case let .connected(tunnelRelay, isPostQuantum):
+ "connected \(isPostQuantum ? "(PQ) " : "")to \(tunnelRelay.hostname)"
case let .disconnecting(actionAfterDisconnect):
"disconnecting and then \(actionAfterDisconnect)"
case .disconnected:
"disconnected"
- case let .reconnecting(tunnelRelay):
- "reconnecting to \(tunnelRelay.hostname)"
+ case let .reconnecting(tunnelRelay, isPostQuantum):
+ "reconnecting \(isPostQuantum ? "(PQ) " : "")to \(tunnelRelay.hostname)"
case .waitingForConnectivity:
"waiting for connectivity"
case let .error(blockedStateReason):
"error state: \(blockedStateReason)"
- #if DEBUG
- case let .negotiatingKey(tunnelRelay):
+ case let .negotiatingPostQuantumKey(tunnelRelay, _):
"negotiating key with \(tunnelRelay.hostname)"
- #endif
}
}
var isSecured: Bool {
switch self {
case .reconnecting, .connecting, .connected, .waitingForConnectivity(.noConnection), .error(.accountExpired),
- .error(.deviceRevoked):
+ .error(.deviceRevoked), .negotiatingPostQuantumKey:
true
case .pendingReconnect, .disconnecting, .disconnected, .waitingForConnectivity(.noNetwork), .error:
false
- #if DEBUG
- case .negotiatingKey:
- false
- #endif
}
}
var relay: SelectedRelay? {
switch self {
- case let .connected(relay), let .reconnecting(relay):
- relay
- case let .connecting(relay):
+ case let .connected(relay, _), let .reconnecting(relay, _), let .negotiatingPostQuantumKey(relay, _):
relay
- #if DEBUG
- case let .negotiatingKey(relay):
+ case let .connecting(relay, _):
relay
- #endif
case .disconnecting, .disconnected, .waitingForConnectivity, .pendingReconnect, .error:
nil
}
diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift
index 8635bc1b9d..92acdb7a55 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift
@@ -27,7 +27,7 @@ private enum TunnelControlActionButton {
}
final class TunnelControlView: UIView {
- private let secureLabel = makeBoldTextLabel(ofSize: 20)
+ private let secureLabel = makeBoldTextLabel(ofSize: 20, numberOfLines: 0)
private let cityLabel = makeBoldTextLabel(ofSize: 34)
private let countryLabel = makeBoldTextLabel(ofSize: 34)
@@ -420,11 +420,12 @@ final class TunnelControlView: UIView {
)
}
- private class func makeBoldTextLabel(ofSize fontSize: CGFloat) -> UILabel {
+ private class func makeBoldTextLabel(ofSize fontSize: CGFloat, numberOfLines: Int = 1) -> UILabel {
let textLabel = UILabel()
textLabel.translatesAutoresizingMaskIntoConstraints = false
textLabel.font = UIFont.boldSystemFont(ofSize: fontSize)
textLabel.textColor = .white
+ textLabel.numberOfLines = numberOfLines
return textLabel
}
@@ -452,225 +453,6 @@ final class TunnelControlView: UIView {
}
private extension TunnelState {
- var textColorForSecureLabel: UIColor {
- switch self {
- case .connecting, .reconnecting, .waitingForConnectivity(.noConnection):
- .white
-
- #if DEBUG
- case .negotiatingKey:
- .white
- #endif
-
- case .connected:
- .successColor
-
- case .disconnecting, .disconnected, .pendingReconnect, .waitingForConnectivity(.noNetwork), .error:
- .dangerColor
- }
- }
-
- var shouldEnableButtons: Bool {
- if case .waitingForConnectivity(.noNetwork) = self {
- return false
- }
-
- return true
- }
-
- var localizedTitleForSecureLabel: String {
- switch self {
- case .connecting, .reconnecting:
- NSLocalizedString(
- "TUNNEL_STATE_CONNECTING",
- tableName: "Main",
- value: "Creating secure connection",
- comment: ""
- )
-
- #if DEBUG
- case .negotiatingKey:
- NSLocalizedString(
- "TUNNEL_STATE_NEGOTIATING_KEY",
- tableName: "Main",
- value: "Negotiating key",
- comment: ""
- )
- #endif
-
- case .connected:
- NSLocalizedString(
- "TUNNEL_STATE_CONNECTED",
- tableName: "Main",
- value: "Secure connection",
- comment: ""
- )
-
- case .disconnecting(.nothing):
- NSLocalizedString(
- "TUNNEL_STATE_DISCONNECTING",
- tableName: "Main",
- value: "Disconnecting",
- comment: ""
- )
- case .disconnecting(.reconnect), .pendingReconnect:
- NSLocalizedString(
- "TUNNEL_STATE_PENDING_RECONNECT",
- tableName: "Main",
- value: "Reconnecting",
- comment: ""
- )
-
- case .disconnected:
- NSLocalizedString(
- "TUNNEL_STATE_DISCONNECTED",
- tableName: "Main",
- value: "Unsecured connection",
- comment: ""
- )
-
- case .waitingForConnectivity(.noConnection), .error:
- NSLocalizedString(
- "TUNNEL_STATE_WAITING_FOR_CONNECTIVITY",
- tableName: "Main",
- value: "Blocked connection",
- comment: ""
- )
-
- case .waitingForConnectivity(.noNetwork):
- NSLocalizedString(
- "TUNNEL_STATE_NO_NETWORK",
- tableName: "Main",
- value: "No network",
- comment: ""
- )
- }
- }
-
- var localizedTitleForSelectLocationButton: String? {
- switch self {
- case .disconnecting(.reconnect), .pendingReconnect:
- NSLocalizedString(
- "SWITCH_LOCATION_BUTTON_TITLE",
- tableName: "Main",
- value: "Select location",
- comment: ""
- )
-
- case .disconnected, .disconnecting(.nothing):
- NSLocalizedString(
- "SELECT_LOCATION_BUTTON_TITLE",
- tableName: "Main",
- value: "Select location",
- comment: ""
- )
-
- case .connecting, .connected, .reconnecting, .waitingForConnectivity, .error:
- NSLocalizedString(
- "SWITCH_LOCATION_BUTTON_TITLE",
- tableName: "Main",
- value: "Switch location",
- comment: ""
- )
-
- #if DEBUG
- case .negotiatingKey:
- NSLocalizedString(
- "SWITCH_LOCATION_BUTTON_TITLE",
- tableName: "Main",
- value: "Switch location",
- comment: ""
- )
- #endif
- }
- }
-
- var localizedAccessibilityLabel: String {
- switch self {
- case .connecting:
- NSLocalizedString(
- "TUNNEL_STATE_CONNECTING_ACCESSIBILITY_LABEL",
- tableName: "Main",
- value: "Creating secure connection",
- comment: ""
- )
-
- #if DEBUG
- case .negotiatingKey:
- NSLocalizedString(
- "TUNNEL_STATE_CONNECTING_ACCESSIBILITY_LABEL",
- tableName: "Main",
- value: "Creating secure connection",
- comment: ""
- )
- #endif
-
- case let .connected(tunnelInfo):
- String(
- format: NSLocalizedString(
- "TUNNEL_STATE_CONNECTED_ACCESSIBILITY_LABEL",
- tableName: "Main",
- value: "Secure connection. Connected to %@, %@",
- comment: ""
- ),
- tunnelInfo.location.city,
- tunnelInfo.location.country
- )
-
- case .disconnected:
- NSLocalizedString(
- "TUNNEL_STATE_DISCONNECTED_ACCESSIBILITY_LABEL",
- tableName: "Main",
- value: "Unsecured connection",
- comment: ""
- )
-
- case let .reconnecting(tunnelInfo):
- String(
- format: NSLocalizedString(
- "TUNNEL_STATE_RECONNECTING_ACCESSIBILITY_LABEL",
- tableName: "Main",
- value: "Reconnecting to %@, %@",
- comment: ""
- ),
- tunnelInfo.location.city,
- tunnelInfo.location.country
- )
-
- case .waitingForConnectivity(.noConnection), .error:
- NSLocalizedString(
- "TUNNEL_STATE_WAITING_FOR_CONNECTIVITY_ACCESSIBILITY_LABEL",
- tableName: "Main",
- value: "Blocked connection",
- comment: ""
- )
-
- case .waitingForConnectivity(.noNetwork):
- NSLocalizedString(
- "TUNNEL_STATE_NO_NETWORK_ACCESSIBILITY_LABEL",
- tableName: "Main",
- value: "No network",
- comment: ""
- )
-
- case .disconnecting(.nothing):
- NSLocalizedString(
- "TUNNEL_STATE_DISCONNECTING_ACCESSIBILITY_LABEL",
- tableName: "Main",
- value: "Disconnecting",
- comment: ""
- )
-
- case .disconnecting(.reconnect), .pendingReconnect:
- NSLocalizedString(
- "TUNNEL_STATE_PENDING_RECONNECT_ACCESSIBILITY_LABEL",
- tableName: "Main",
- value: "Reconnecting",
- comment: ""
- )
- }
- }
-
func actionButtons(traitCollection: UITraitCollection) -> [TunnelControlActionButton] {
switch (traitCollection.userInterfaceIdiom, traitCollection.horizontalSizeClass) {
case (.phone, _), (.pad, .compact):
@@ -682,10 +464,8 @@ private extension TunnelState {
.waitingForConnectivity(.noConnection):
[.selectLocation, .cancel]
- #if DEBUG
- case .negotiatingKey:
+ case .negotiatingPostQuantumKey:
[.selectLocation, .cancel]
- #endif
case .connected, .reconnecting, .error:
[.selectLocation, .disconnect]
@@ -700,10 +480,8 @@ private extension TunnelState {
.waitingForConnectivity(.noConnection):
[.cancel]
- #if DEBUG
- case .negotiatingKey:
+ case .negotiatingPostQuantumKey:
[.cancel]
- #endif
case .connected, .reconnecting, .error:
[.disconnect]
}
diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift
index 7d68b3dd41..36f8535047 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift
@@ -147,24 +147,17 @@ class TunnelViewController: UIViewController, RootContainment {
private func updateMap(animated: Bool) {
switch tunnelState {
- case let .connecting(tunnelRelay):
+ case let .connecting(tunnelRelay, _):
mapViewController.removeLocationMarker()
contentView.setAnimatingActivity(true)
mapViewController.setCenter(tunnelRelay?.location.geoCoordinate, animated: animated)
- case let .reconnecting(tunnelRelay):
+ case let .reconnecting(tunnelRelay, _), let .negotiatingPostQuantumKey(tunnelRelay, _):
mapViewController.removeLocationMarker()
contentView.setAnimatingActivity(true)
mapViewController.setCenter(tunnelRelay.location.geoCoordinate, animated: animated)
- #if DEBUG
- case let .negotiatingKey(tunnelRelay):
- mapViewController.removeLocationMarker()
- contentView.setAnimatingActivity(true)
- mapViewController.setCenter(tunnelRelay.location.geoCoordinate, animated: animated)
- #endif
-
- case let .connected(tunnelRelay):
+ case let .connected(tunnelRelay, _):
let center = tunnelRelay.location.geoCoordinate
mapViewController.setCenter(center, animated: animated) {
self.contentView.setAnimatingActivity(false)
diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift
index bd3b42b76a..2934e7d457 100644
--- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift
+++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift
@@ -170,7 +170,6 @@ final class VPNSettingsCellFactory: CellFactoryProtocol {
cell.accessibilityIdentifier = "\(item.accessibilityIdentifier.rawValue)\(portString)"
cell.applySubCellStyling()
- #if DEBUG
case .quantumResistanceAutomatic:
guard let cell = cell as? SelectableSettingsCell else { return }
@@ -205,7 +204,6 @@ final class VPNSettingsCellFactory: CellFactoryProtocol {
)
cell.accessibilityIdentifier = item.accessibilityIdentifier
cell.applySubCellStyling()
- #endif
}
}
}
diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift
index 4525b7d886..2891fc4d5f 100644
--- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift
+++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift
@@ -57,9 +57,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
case wireGuardPorts
case wireGuardObfuscation
case wireGuardObfuscationPort
- #if DEBUG
case quantumResistance
- #endif
}
enum Item: Hashable {
@@ -71,11 +69,9 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
case wireGuardObfuscationOn
case wireGuardObfuscationOff
case wireGuardObfuscationPort(_ port: UInt16)
- #if DEBUG
case quantumResistanceAutomatic
case quantumResistanceOn
case quantumResistanceOff
- #endif
static var wireGuardPorts: [Item] {
let defaultPorts = VPNSettingsViewModel.defaultWireGuardPorts.map {
@@ -92,11 +88,9 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
[.wireGuardObfuscationPort(0), wireGuardObfuscationPort(80), wireGuardObfuscationPort(5001)]
}
- #if DEBUG
static var quantumResistance: [Item] {
[.quantumResistanceAutomatic, .quantumResistanceOn, .quantumResistanceOff]
}
- #endif
var accessibilityIdentifier: AccessibilityIdentifier {
switch self {
@@ -116,14 +110,12 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
return .wireGuardObfuscationOff
case .wireGuardObfuscationPort:
return .wireGuardObfuscationPort
- #if DEBUG
case .quantumResistanceAutomatic:
return .quantumResistanceAutomatic
case .quantumResistanceOn:
return .quantumResistanceOn
case .quantumResistanceOff:
return .quantumResistanceOff
- #endif
}
}
@@ -141,10 +133,8 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
return .wireGuardObfuscation
case .wireGuardObfuscationPort:
return .wireGuardObfuscationPort
- #if DEBUG
case .quantumResistanceAutomatic, .quantumResistanceOn, .quantumResistanceOff:
return .quantumResistance
- #endif
}
}
}
@@ -167,30 +157,20 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
case .off: .wireGuardObfuscationOff
case .on: .wireGuardObfuscationOn
}
- #if DEBUG
let quantumResistanceItem: Item = switch viewModel.quantumResistance {
case .automatic: .quantumResistanceAutomatic
case .off: .quantumResistanceOff
case .on: .quantumResistanceOn
}
- #endif
let obfuscationPortItem: Item = .wireGuardObfuscationPort(viewModel.obfuscationPort.portValue)
- #if DEBUG
return [
wireGuardPortItem,
obfuscationStateItem,
obfuscationPortItem,
quantumResistanceItem,
].compactMap { indexPath(for: $0) }
- #else
- return [
- wireGuardPortItem,
- obfuscationStateItem,
- obfuscationPortItem,
- ].compactMap { indexPath(for: $0) }
- #endif
}
init(tableView: UITableView) {
@@ -299,7 +279,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
selectObfuscationPort(port)
delegate?.didChangeViewModel(viewModel)
- #if DEBUG
case .quantumResistanceAutomatic:
selectQuantumResistance(.automatic)
delegate?.didChangeViewModel(viewModel)
@@ -309,7 +288,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
case .quantumResistanceOff:
selectQuantumResistance(.off)
delegate?.didChangeViewModel(viewModel)
- #endif
default:
break
}
@@ -347,11 +325,9 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
case .wireGuardObfuscationPort:
configureObfuscationPortHeader(view)
return view
- #if DEBUG
case .quantumResistance:
configureQuantumResistanceHeader(view)
return view
- #endif
default:
return nil
@@ -548,7 +524,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
}
}
- #if DEBUG
private func configureQuantumResistanceHeader(_ header: SettingsHeaderView) {
let title = NSLocalizedString(
"QUANTUM_RESISTANCE_HEADER_LABEL",
@@ -577,7 +552,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
self.map { $0.delegate?.showInfo(for: .quantumResistance) }
}
}
- #endif
private func selectRow(at indexPath: IndexPath?, animated: Bool = false) {
tableView?.selectRow(at: indexPath, animated: animated, scrollPosition: .none)
diff --git a/ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelStateTests.swift b/ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelStateTests.swift
new file mode 100644
index 0000000000..9a707fb30d
--- /dev/null
+++ b/ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelStateTests.swift
@@ -0,0 +1,139 @@
+//
+// TunnelStateTests.swift
+// MullvadVPNTests
+//
+// Created by Andrew Bulhak on 2024-05-03.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import MullvadTypes
+import PacketTunnelCore
+import XCTest
+
+final class TunnelStateTests: XCTestCase {
+ let arbitrarySelectedRelay = SelectedRelay(
+ endpoint: MullvadEndpoint(
+ ipv4Relay: IPv4Endpoint(ip: .any, port: 0),
+ ipv4Gateway: .any,
+ ipv6Gateway: .any,
+ publicKey: Data()
+ ),
+ hostname: "hostname-goes-here",
+ location: Location(country: "country", countryCode: "", city: "city", cityCode: "", latitude: 0, longitude: 0),
+ retryAttempts: 0
+ )
+
+ // MARK: description
+
+ func testDescription_Connecting_NoRelay() {
+ XCTAssertEqual(
+ TunnelState.connecting(nil, isPostQuantum: false).description,
+ "connecting, fetching relay"
+ )
+
+ XCTAssertEqual(
+ TunnelState.connecting(nil, isPostQuantum: true).description,
+ "connecting (PQ), fetching relay"
+ )
+ }
+
+ func testDescription_Connecting_WithRelay() {
+ XCTAssertEqual(
+ TunnelState.connecting(arbitrarySelectedRelay, isPostQuantum: false).description,
+ "connecting to hostname-goes-here"
+ )
+
+ XCTAssertEqual(
+ TunnelState.connecting(arbitrarySelectedRelay, isPostQuantum: true).description,
+ "connecting (PQ) to hostname-goes-here"
+ )
+ }
+
+ func testDescription_Connected() {
+ XCTAssertEqual(
+ TunnelState.connected(arbitrarySelectedRelay, isPostQuantum: false).description,
+ "connected to hostname-goes-here"
+ )
+
+ XCTAssertEqual(
+ TunnelState.connected(arbitrarySelectedRelay, isPostQuantum: true).description,
+ "connected (PQ) to hostname-goes-here"
+ )
+ }
+
+ // MARK: localizedTitleForSecureLabel
+
+ func testLocalizedTitleForSecureLabel_Connecting() {
+ XCTAssertEqual(
+ TunnelState.connecting(nil, isPostQuantum: false).localizedTitleForSecureLabel,
+ "Creating secure connection"
+ )
+
+ XCTAssertEqual(
+ TunnelState.connecting(nil, isPostQuantum: true).localizedTitleForSecureLabel,
+ "Creating quantum secure connection"
+ )
+ }
+
+ func testLocalizedTitleForSecureLabel_Reconnecting() {
+ XCTAssertEqual(
+ TunnelState.reconnecting(arbitrarySelectedRelay, isPostQuantum: false).localizedTitleForSecureLabel,
+ "Creating secure connection"
+ )
+
+ XCTAssertEqual(
+ TunnelState.reconnecting(arbitrarySelectedRelay, isPostQuantum: true).localizedTitleForSecureLabel,
+ "Creating quantum secure connection"
+ )
+ }
+
+ func testLocalizedTitleForSecureLabel_Connected() {
+ XCTAssertEqual(
+ TunnelState.connected(arbitrarySelectedRelay, isPostQuantum: false).localizedTitleForSecureLabel,
+ "Secure connection"
+ )
+
+ XCTAssertEqual(
+ TunnelState.connected(arbitrarySelectedRelay, isPostQuantum: true).localizedTitleForSecureLabel,
+ "Quantum secure connection"
+ )
+ }
+
+ // MARK: localizedAccessibilityLabel
+
+ func testLocalizedAccessibilityLabel_Connecting() {
+ XCTAssertEqual(
+ TunnelState.connecting(nil, isPostQuantum: false).localizedAccessibilityLabel,
+ "Creating secure connection"
+ )
+
+ XCTAssertEqual(
+ TunnelState.connecting(nil, isPostQuantum: true).localizedAccessibilityLabel,
+ "Creating quantum secure connection"
+ )
+ }
+
+ func testLocalizedAccessibilityLabel_Reconnecting() {
+ XCTAssertEqual(
+ TunnelState.reconnecting(arbitrarySelectedRelay, isPostQuantum: false).localizedAccessibilityLabel,
+ "Reconnecting to city, country"
+ )
+
+ XCTAssertEqual(
+ TunnelState.reconnecting(arbitrarySelectedRelay, isPostQuantum: true).localizedAccessibilityLabel,
+ "Reconnecting to city, country"
+ )
+ }
+
+ func testLocalizedAccessibilityLabel_Connected() {
+ XCTAssertEqual(
+ TunnelState.connected(arbitrarySelectedRelay, isPostQuantum: false).localizedAccessibilityLabel,
+ "Secure connection. Connected to city, country"
+ )
+
+ XCTAssertEqual(
+ TunnelState.connected(arbitrarySelectedRelay, isPostQuantum: true).localizedAccessibilityLabel,
+ "Quantum secure connection. Connected to city, country"
+ )
+ }
+}
diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
index a635348773..6b27aa59f8 100644
--- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
+++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
@@ -8,6 +8,7 @@
import Foundation
import MullvadLogging
+import MullvadPostQuantum
import MullvadREST
import MullvadSettings
import MullvadTypes
@@ -22,9 +23,12 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
private let constraintsUpdater = RelayConstraintsUpdater()
private var actor: PacketTunnelActor!
+ private var postQuantumActor: PostQuantumKeyExchangeActor!
private var appMessageHandler: AppMessageHandler!
private var stateObserverTask: AnyTask?
private var deviceChecker: DeviceChecker!
+ private var adapter: WgAdapter!
+ private var relaySelector: RelaySelectorWrapper!
override init() {
Self.configureLogging()
@@ -48,7 +52,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
addressCache: addressCache
)
- let adapter = WgAdapter(packetTunnelProvider: self)
+ adapter = WgAdapter(packetTunnelProvider: self)
let tunnelMonitor = TunnelMonitor(
eventQueue: internalQueue,
@@ -65,6 +69,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
let devicesProxy = proxyFactory.createDevicesProxy()
deviceChecker = DeviceChecker(accountsProxy: accountsProxy, devicesProxy: devicesProxy)
+ relaySelector = RelaySelectorWrapper(relayCache: ipOverrideWrapper)
actor = PacketTunnelActor(
timings: PacketTunnelActorTimings(),
@@ -72,11 +77,16 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
tunnelMonitor: tunnelMonitor,
defaultPathObserver: PacketTunnelPathObserver(packetTunnelProvider: self, eventQueue: internalQueue),
blockedStateErrorMapper: BlockedStateErrorMapper(),
- relaySelector: RelaySelectorWrapper(relayCache: ipOverrideWrapper),
+ relaySelector: relaySelector,
settingsReader: SettingsReader(),
protocolObfuscator: ProtocolObfuscator<UDPOverTCPObfuscator>()
)
+ postQuantumActor = PostQuantumKeyExchangeActor(
+ packetTunnel: self,
+ onFailure: self.keyExchangeFailed
+ )
+
let urlRequestProxy = URLRequestProxy(dispatchQueue: internalQueue, transportProvider: transportProvider)
appMessageHandler = AppMessageHandler(packetTunnelActor: actor, urlRequestProxy: urlRequestProxy)
@@ -104,6 +114,12 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
if connectionState.connectionAttemptCount > 1 {
return
}
+ case .negotiatingPostQuantumKey:
+ // When negotiating post quantum keys, allow the connection to go through immediately.
+ // Otherwise, the in-tunnel TCP connection will never become ready as the OS doesn't let
+ // any traffic through until this function returns, which would prevent negotiating keys
+ // from an unconnected state.
+ return
default:
break
}
@@ -235,10 +251,9 @@ extension PacketTunnelProvider {
// Cache last connection attempt to filter out repeating calls.
lastConnectionAttempt = connectionAttempt
- #if DEBUG
- case .negotiatingKey:
- break
- #endif
+ case let .negotiatingPostQuantumKey(_, privateKey):
+ postQuantumActor.endCurrentNegotiation()
+ postQuantumActor.startNegotiation(with: privateKey)
case .initial, .connected, .disconnecting, .disconnected, .error:
break
@@ -283,8 +298,15 @@ extension PacketTunnelProvider {
}
extension PacketTunnelProvider: PostQuantumKeyReceiving {
- func receivePostQuantumKey(_ key: PreSharedKey) {
- // TODO: send the key to the actor
- actor.replacePreSharedKey(key)
+ func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) {
+ 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)
}
}
diff --git a/ios/PacketTunnel/PacketTunnelProvider/SettingsReader.swift b/ios/PacketTunnel/PacketTunnelProvider/SettingsReader.swift
index 10282b5ff3..ade3b5a3af 100644
--- a/ios/PacketTunnel/PacketTunnelProvider/SettingsReader.swift
+++ b/ios/PacketTunnel/PacketTunnelProvider/SettingsReader.swift
@@ -21,7 +21,8 @@ struct SettingsReader: SettingsReaderProtocol {
interfaceAddresses: [deviceData.ipv4Address, deviceData.ipv6Address],
relayConstraints: settings.relayConstraints,
dnsServers: settings.dnsSettings.selectedDNSServers,
- obfuscation: settings.wireGuardObfuscation
+ obfuscation: settings.wireGuardObfuscation,
+ quantumResistance: settings.tunnelQuantumResistance
)
}
}
diff --git a/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift
new file mode 100644
index 0000000000..33291d8ea5
--- /dev/null
+++ b/ios/PacketTunnel/PostQuantumKeyExchangeActor.swift
@@ -0,0 +1,107 @@
+//
+// PostQuantumKeyExchangeActor.swift
+// PacketTunnel
+//
+// Created by Marco Nikic on 2024-04-12.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import MullvadPostQuantum
+import NetworkExtension
+import WireGuardKitTypes
+
+class PostQuantumKeyExchangeActor {
+ struct Negotiation {
+ var negotiator: PostQuantumKeyNegotiator
+ var inTunnelTCPConnection: NWTCPConnection
+ var tcpConnectionObserver: NSKeyValueObservation
+
+ func cancel() {
+ negotiator.cancelKeyNegotiation()
+ tcpConnectionObserver.invalidate()
+ inTunnelTCPConnection.cancel()
+ }
+ }
+
+ unowned let packetTunnel: PacketTunnelProvider
+ private var negotiation: Negotiation?
+ private var timer: DispatchSourceTimer?
+
+ // Callback in the event of the negotiation failing on startup
+ var onFailure: () -> Void
+
+ init(packetTunnel: PacketTunnelProvider, onFailure: @escaping (() -> Void)) {
+ self.packetTunnel = packetTunnel
+ self.onFailure = onFailure
+ }
+
+ private func createTCPConnection(_ gatewayEndpoint: NWHostEndpoint) -> NWTCPConnection {
+ self.packetTunnel.createTCPConnectionThroughTunnel(
+ to: gatewayEndpoint,
+ enableTLS: false,
+ tlsParameters: nil,
+ delegate: nil
+ )
+ }
+
+ func startNegotiation(with privateKey: PrivateKey) {
+ let negotiator = PostQuantumKeyNegotiator()
+
+ let gatewayAddress = "10.64.0.1"
+ let IPv4Gateway = IPv4Address(gatewayAddress)!
+ let endpoint = NWHostEndpoint(hostname: gatewayAddress, port: "\(CONFIG_SERVICE_PORT)")
+ let inTunnelTCPConnection = createTCPConnection(endpoint)
+
+ // This will become the new private key of the device
+ let ephemeralSharedKey = PrivateKey()
+
+ // If the connection never becomes viable, force a reconnection after 10 seconds
+ scheduleInTunnelConnectionTimeout(startTime: .now() + 10)
+
+ let tcpConnectionObserver = inTunnelTCPConnection.observe(\.isViable, options: [
+ .initial,
+ .new,
+ ]) { [weak self] observedConnection, _ in
+ guard let self, observedConnection.isViable else { return }
+ self.negotiation?.tcpConnectionObserver.invalidate()
+ self.timer?.cancel()
+
+ if !negotiator.startNegotiation(
+ gatewayIP: IPv4Gateway,
+ devicePublicKey: privateKey.publicKey,
+ presharedKey: ephemeralSharedKey,
+ packetTunnel: packetTunnel,
+ tcpConnection: inTunnelTCPConnection
+ ) {
+ self.negotiation = nil
+ self.onFailure()
+ }
+ }
+ negotiation = Negotiation(
+ negotiator: negotiator,
+ inTunnelTCPConnection: inTunnelTCPConnection,
+ tcpConnectionObserver: tcpConnectionObserver
+ )
+ }
+
+ func endCurrentNegotiation() {
+ negotiation?.cancel()
+ negotiation = nil
+ }
+
+ 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/PacketTunnelCore/Actor/ConfigurationBuilder.swift b/ios/PacketTunnelCore/Actor/ConfigurationBuilder.swift
index 06b970a223..226b8b05fe 100644
--- a/ios/PacketTunnelCore/Actor/ConfigurationBuilder.swift
+++ b/ios/PacketTunnelCore/Actor/ConfigurationBuilder.swift
@@ -21,14 +21,31 @@ public struct PublicKeyError: LocalizedError {
}
/// Struct building tunnel adapter configuration.
-struct ConfigurationBuilder {
+public struct ConfigurationBuilder {
var privateKey: PrivateKey
var interfaceAddresses: [IPAddressRange]
var dns: SelectedDNSServers?
var endpoint: MullvadEndpoint?
var allowedIPs: [IPAddressRange]
+ var preSharedKey: PreSharedKey?
- func makeConfiguration() throws -> TunnelAdapterConfiguration {
+ public init(
+ privateKey: PrivateKey,
+ interfaceAddresses: [IPAddressRange],
+ dns: SelectedDNSServers? = nil,
+ endpoint: MullvadEndpoint? = nil,
+ allowedIPs: [IPAddressRange],
+ preSharedKey: PreSharedKey? = nil
+ ) {
+ self.privateKey = privateKey
+ self.interfaceAddresses = interfaceAddresses
+ self.dns = dns
+ self.endpoint = endpoint
+ self.allowedIPs = allowedIPs
+ self.preSharedKey = preSharedKey
+ }
+
+ public func makeConfiguration() throws -> TunnelAdapterConfiguration {
return TunnelAdapterConfiguration(
privateKey: privateKey,
interfaceAddresses: interfaceAddresses,
@@ -48,7 +65,8 @@ struct ConfigurationBuilder {
return TunnelPeer(
endpoint: .ipv4(endpoint.ipv4Relay),
- publicKey: publicKey
+ publicKey: publicKey,
+ preSharedKey: preSharedKey
)
}
}
diff --git a/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift b/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift
index 1504f3e47d..08f023a2d8 100644
--- a/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift
+++ b/ios/PacketTunnelCore/Actor/ObservedState+Extensions.swift
@@ -12,13 +12,10 @@ import MullvadTypes
extension ObservedState {
public var relayConstraints: RelayConstraints? {
switch self {
- case let .connecting(connState), let .connected(connState), let .reconnecting(connState):
+ case let .connecting(connState), let .connected(connState), let .reconnecting(connState),
+ let .negotiatingPostQuantumKey(connState, _):
connState.relayConstraints
- #if DEBUG
- case let .negotiatingKey(connState):
- connState.relayConstraints
- #endif
case let .error(blockedState):
blockedState.relayConstraints
@@ -33,10 +30,8 @@ extension ObservedState {
"Connected"
case .connecting:
"Connecting"
- #if DEBUG
- case .negotiatingKey:
- "Negotiating key"
- #endif
+ case .negotiatingPostQuantumKey:
+ "Negotiating Post Quantum Secure Key"
case .reconnecting:
"Reconnecting"
case .disconnecting:
@@ -56,6 +51,7 @@ extension ObservedState {
let .connecting(connectionState),
let .reconnecting(connectionState),
let .connected(connectionState),
+ let .negotiatingPostQuantumKey(connectionState, _),
let .disconnecting(connectionState):
connectionState
default:
diff --git a/ios/PacketTunnelCore/Actor/ObservedState.swift b/ios/PacketTunnelCore/Actor/ObservedState.swift
index 3f92b300e5..bdb85a8e51 100644
--- a/ios/PacketTunnelCore/Actor/ObservedState.swift
+++ b/ios/PacketTunnelCore/Actor/ObservedState.swift
@@ -10,15 +10,14 @@ import Combine
import Foundation
import MullvadTypes
import Network
+import WireGuardKitTypes
/// A serializable representation of internal state.
public enum ObservedState: Equatable, Codable {
case initial
case connecting(ObservedConnectionState)
case reconnecting(ObservedConnectionState)
- #if DEBUG
- case negotiatingKey(ObservedConnectionState)
- #endif
+ case negotiatingPostQuantumKey(ObservedConnectionState, PrivateKey)
case connected(ObservedConnectionState)
case disconnecting(ObservedConnectionState)
case disconnected
@@ -34,6 +33,7 @@ public struct ObservedConnectionState: Equatable, Codable {
public var transportLayer: TransportLayer
public var remotePort: UInt16
public var lastKeyRotation: Date?
+ public let isPostQuantum: Bool
public var isNetworkReachable: Bool {
networkReachability != .unreachable
@@ -46,7 +46,8 @@ public struct ObservedConnectionState: Equatable, Codable {
connectionAttemptCount: UInt,
transportLayer: TransportLayer,
remotePort: UInt16,
- lastKeyRotation: Date? = nil
+ lastKeyRotation: Date? = nil,
+ isPostQuantum: Bool
) {
self.selectedRelay = selectedRelay
self.relayConstraints = relayConstraints
@@ -55,6 +56,7 @@ public struct ObservedConnectionState: Equatable, Codable {
self.transportLayer = transportLayer
self.remotePort = remotePort
self.lastKeyRotation = lastKeyRotation
+ self.isPostQuantum = isPostQuantum
}
}
@@ -78,6 +80,8 @@ extension State {
return .reconnecting(connState.observedConnectionState)
case let .disconnecting(connState):
return .disconnecting(connState.observedConnectionState)
+ case let .negotiatingPostQuantumKey(connState, privateKey):
+ return .negotiatingPostQuantumKey(connState.observedConnectionState, privateKey)
case .disconnected:
return .disconnected
case let .error(blockedState):
@@ -96,7 +100,8 @@ extension State.ConnectionData {
connectionAttemptCount: connectionAttemptCount,
transportLayer: transportLayer,
remotePort: remotePort,
- lastKeyRotation: lastKeyRotation
+ lastKeyRotation: lastKeyRotation,
+ isPostQuantum: isPostQuantum
)
}
}
diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor+ConnectionMonitoring.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor+ConnectionMonitoring.swift
index c2bc921b05..84d34d2650 100644
--- a/ios/PacketTunnelCore/Actor/PacketTunnelActor+ConnectionMonitoring.swift
+++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor+ConnectionMonitoring.swift
@@ -42,7 +42,7 @@ extension PacketTunnelActor {
connState.connectionAttemptCount = 0
state = .connected(connState)
- case .initial, .connected, .disconnecting, .disconnected, .error:
+ case .initial, .connected, .disconnecting, .disconnected, .error, .negotiatingPostQuantumKey:
break
}
}
@@ -53,7 +53,7 @@ extension PacketTunnelActor {
case .connecting, .reconnecting, .connected:
commandChannel.send(.reconnect(.random, reason: .connectionLoss))
- case .initial, .disconnected, .disconnecting, .error:
+ case .initial, .disconnected, .disconnecting, .error, .negotiatingPostQuantumKey:
break
}
}
diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor+ErrorState.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor+ErrorState.swift
index b450f44819..fd88ea94e4 100644
--- a/ios/PacketTunnelCore/Actor/PacketTunnelActor+ErrorState.swift
+++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor+ErrorState.swift
@@ -84,7 +84,8 @@ extension PacketTunnelActor {
return nil
}
- case .disconnecting, .disconnected:
+ // Post quantum key exchange cannot enter the blocked state
+ case .disconnecting, .disconnected, .negotiatingPostQuantumKey:
return nil
}
}
diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor+PostQuantum.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor+PostQuantum.swift
new file mode 100644
index 0000000000..53066ce437
--- /dev/null
+++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor+PostQuantum.swift
@@ -0,0 +1,84 @@
+//
+// PacketTunnelActor+PostQuantum.swift
+// PacketTunnelCore
+//
+// Created by Andrew Bulhak on 2024-05-13.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import WireGuardKitTypes
+
+extension PacketTunnelActor {
+ /**
+ Attempt to start the process of negotiating a post-quantum secure key, setting up an initial
+ connection restricted to the negotiation host and entering the negotiating state.
+ */
+ internal func tryStartPostQuantumNegotiation(
+ withSettings settings: Settings,
+ nextRelay: NextRelay,
+ reason: ReconnectReason
+ ) async throws {
+ if let connectionState = try makeConnectionState(nextRelay: nextRelay, settings: settings, reason: reason) {
+ let selectedEndpoint = connectionState.selectedRelay.endpoint
+ let activeKey = activeKey(from: connectionState, in: settings)
+
+ let configurationBuilder = ConfigurationBuilder(
+ privateKey: activeKey,
+ interfaceAddresses: settings.interfaceAddresses,
+ dns: settings.dnsServers,
+ endpoint: selectedEndpoint,
+ allowedIPs: [
+ IPAddressRange(from: "10.64.0.1/32")!,
+ ]
+ )
+
+ try await tunnelAdapter.start(configuration: configurationBuilder.makeConfiguration())
+ state = .negotiatingPostQuantumKey(connectionState, activeKey)
+ }
+ }
+
+ /**
+ Called on receipt of the new PQ-negotiated key, to reconnect to the relay, in PQ-secure mode.
+ */
+ internal func postQuantumConnect(with key: PreSharedKey, privateKey: PrivateKey) async {
+ guard
+ // It is important to select the same relay that was saved in the connection state as the key negotiation happened with this specific relay.
+ let selectedRelay = state.connectionData?.selectedRelay,
+ let settings: Settings = try? settingsReader.read(),
+ let connectionState = try? obfuscateConnection(
+ nextRelay: .preSelected(selectedRelay),
+ settings: settings,
+ reason: .userInitiated
+ )
+ else {
+ logger.error("Could not create connection state in PostQuantumConnect")
+
+ let nextRelay: NextRelay = (state.connectionData?.selectedRelay).map { .preSelected($0) } ?? .current
+ commandChannel.send(.reconnect(nextRelay))
+ return
+ }
+
+ let configurationBuilder = ConfigurationBuilder(
+ privateKey: privateKey,
+ interfaceAddresses: settings.interfaceAddresses,
+ dns: settings.dnsServers,
+ endpoint: connectionState.connectedEndpoint,
+ allowedIPs: [
+ IPAddressRange(from: "0.0.0.0/0")!,
+ IPAddressRange(from: "::/0")!,
+ ],
+ preSharedKey: key
+ )
+ stopDefaultPathObserver()
+
+ state = .connecting(connectionState)
+
+ try? await tunnelAdapter.start(configuration: configurationBuilder.makeConfiguration())
+ // Resume tunnel monitoring and use IPv4 gateway as a probe address.
+ tunnelMonitor.start(probeAddress: connectionState.selectedRelay.endpoint.ipv4Gateway)
+ // Restart default path observer and notify the observer with the current path that might have changed while
+ // path observer was paused.
+ startDefaultPathObserver(notifyObserverWithCurrentPath: false)
+ }
+}
diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor+Public.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor+Public.swift
index 0d80fb6d58..209a6041d5 100644
--- a/ios/PacketTunnelCore/Actor/PacketTunnelActor+Public.swift
+++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor+Public.swift
@@ -56,8 +56,8 @@ extension PacketTunnelActor {
- Parameter key: the new key
*/
- nonisolated public func replacePreSharedKey(_ key: PreSharedKey) {
- commandChannel.send(.replaceDevicePrivateKey(key))
+ nonisolated public func replacePreSharedKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) {
+ commandChannel.send(.replaceDevicePrivateKey(key, ephemeralKey: ephemeralKey))
}
/**
diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift
index 81b3bfaff1..153c1c0430 100644
--- a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift
+++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift
@@ -43,7 +43,7 @@ public actor PacketTunnelActor {
let tunnelMonitor: TunnelMonitorProtocol
let defaultPathObserver: DefaultPathObserverProtocol
let blockedStateErrorMapper: BlockedStateErrorMapperProtocol
- let relaySelector: RelaySelectorProtocol
+ public let relaySelector: RelaySelectorProtocol
let settingsReader: SettingsReaderProtocol
let protocolObfuscator: ProtocolObfuscation
@@ -113,8 +113,8 @@ public actor PacketTunnelActor {
case let .networkReachability(defaultPath):
await handleDefaultPathChange(defaultPath)
- case .replaceDevicePrivateKey:
- self.logger.warning("Not yet implemented")
+ case let .replaceDevicePrivateKey(preSharedKey, ephemeralKey):
+ await postQuantumConnect(with: preSharedKey, privateKey: ephemeralKey)
}
}
}
@@ -164,7 +164,8 @@ extension PacketTunnelActor {
/// Stop the tunnel.
private func stop() async {
switch state {
- case let .connected(connState), let .connecting(connState), let .reconnecting(connState):
+ case let .connected(connState), let .connecting(connState), let .reconnecting(connState),
+ let .negotiatingPostQuantumKey(connState, _):
state = .disconnecting(connState)
tunnelMonitor.stop()
@@ -199,7 +200,9 @@ extension PacketTunnelActor {
private func reconnect(to nextRelay: NextRelay, reason: ReconnectReason) async {
do {
switch state {
- case .connecting, .connected, .reconnecting, .error:
+ // There is no connection monitoring going on when exchanging keys.
+ // The procedure starts from scratch for each reconnection attempts.
+ case .connecting, .connected, .reconnecting, .error, .negotiatingPostQuantumKey:
switch reason {
case .connectionLoss:
// Tunnel monitor is already paused at this point. Avoid calling stop() to prevent the reset of
@@ -222,9 +225,27 @@ extension PacketTunnelActor {
}
/**
- Attempt to start the tunnel by performing the following steps:
+ Entry point for attempting to start the tunnel by performing the following steps:
+
+ - Read settings
+ - Start either a direct connection or the post-quantum key negotiation process, depending on settings.
+ */
+ private func tryStart(
+ nextRelay: NextRelay,
+ reason: ReconnectReason = .userInitiated
+ ) async throws {
+ let settings: Settings = try settingsReader.read()
+
+ if settings.quantumResistance.isEnabled {
+ try await tryStartPostQuantumNegotiation(withSettings: settings, nextRelay: nextRelay, reason: reason)
+ } else {
+ try await tryStartConnection(withSettings: settings, nextRelay: nextRelay, reason: reason)
+ }
+ }
+
+ /**
+ Attempt to start a direct (non-quantum) connection to the tunnel by performing the following steps:
- - Read settings.
- Determine target state, it can either be `.connecting` or `.reconnecting`. (See `TargetStateForReconnect`)
- Bail if target state cannot be determined. That means that the actor is past the point when it could logically connect or reconnect, i.e it can already be in
`.disconnecting` state.
@@ -236,22 +257,15 @@ extension PacketTunnelActor {
- nextRelay: which relay should be selected next.
- reason: reason for reconnect
*/
- private func tryStart(
- nextRelay: NextRelay = .random,
- reason: ReconnectReason = .userInitiated
+ private func tryStartConnection(
+ withSettings settings: Settings,
+ nextRelay: NextRelay,
+ reason: ReconnectReason
) async throws {
- let settings: Settings = try settingsReader.read()
-
guard let connectionState = try obfuscateConnection(nextRelay: nextRelay, settings: settings, reason: reason),
let targetState = state.targetStateForReconnect else { return }
- let activeKey: PrivateKey
- switch connectionState.keyPolicy {
- case .useCurrent:
- activeKey = settings.privateKey
- case let .usePrior(priorKey, _):
- activeKey = priorKey
- }
+ let activeKey = activeKey(from: connectionState, in: settings)
switch targetState {
case .connecting:
@@ -300,7 +314,7 @@ extension PacketTunnelActor {
- Returns: New connection state or `nil` if current state is at or past `.disconnecting` phase.
*/
- private func makeConnectionState(
+ internal func makeConnectionState(
nextRelay: NextRelay,
settings: Settings,
reason: ReconnectReason
@@ -326,6 +340,14 @@ extension PacketTunnelActor {
connectionState.incrementAttemptCount()
}
fallthrough
+ case var .negotiatingPostQuantumKey(connectionState, _):
+ let selectedRelay = try callRelaySelector(
+ connectionState.selectedRelay,
+ connectionState.connectionAttemptCount
+ )
+ connectionState.selectedRelay = selectedRelay
+ connectionState.relayConstraints = settings.relayConstraints
+ return connectionState
case var .connected(connectionState):
let selectedRelay = try callRelaySelector(
connectionState.selectedRelay,
@@ -353,11 +375,21 @@ extension PacketTunnelActor {
lastKeyRotation: lastKeyRotation,
connectedEndpoint: selectedRelay.endpoint,
transportLayer: .udp,
- remotePort: selectedRelay.endpoint.ipv4Relay.port
+ remotePort: selectedRelay.endpoint.ipv4Relay.port,
+ isPostQuantum: settings.quantumResistance.isEnabled
)
}
- private func obfuscateConnection(
+ internal func activeKey(from state: State.ConnectionData, in settings: Settings) -> PrivateKey {
+ switch state.keyPolicy {
+ case .useCurrent:
+ settings.privateKey
+ case let .usePrior(priorKey, _):
+ priorKey
+ }
+ }
+
+ internal func obfuscateConnection(
nextRelay: NextRelay,
settings: Settings,
reason: ReconnectReason
@@ -382,7 +414,8 @@ extension PacketTunnelActor {
lastKeyRotation: connectionState.lastKeyRotation,
connectedEndpoint: obfuscatedEndpoint,
transportLayer: transportLayer,
- remotePort: protocolObfuscator.remotePort
+ remotePort: protocolObfuscator.remotePort,
+ isPostQuantum: settings.quantumResistance.isEnabled
)
}
diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift
index be325fc7b6..c4bd2c314b 100644
--- a/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift
+++ b/ios/PacketTunnelCore/Actor/PacketTunnelActorCommand.swift
@@ -37,7 +37,7 @@ extension PacketTunnelActor {
case networkReachability(NetworkPath)
/// Update the device private key, as per post-quantum protocols
- case replaceDevicePrivateKey(PreSharedKey)
+ case replaceDevicePrivateKey(PreSharedKey, ephemeralKey: PrivateKey)
/// Format command for log output.
func logFormat() -> String {
diff --git a/ios/PacketTunnelCore/Actor/Protocols/RelaySelectorProtocol.swift b/ios/PacketTunnelCore/Actor/Protocols/RelaySelectorProtocol.swift
index 4224233f70..a4408392e3 100644
--- a/ios/PacketTunnelCore/Actor/Protocols/RelaySelectorProtocol.swift
+++ b/ios/PacketTunnelCore/Actor/Protocols/RelaySelectorProtocol.swift
@@ -36,3 +36,9 @@ public struct SelectedRelay: Equatable, Codable {
self.retryAttempts = retryAttempts
}
}
+
+extension SelectedRelay: CustomDebugStringConvertible {
+ public var debugDescription: String {
+ "\(hostname) -> \(endpoint.ipv4Relay.description)"
+ }
+}
diff --git a/ios/PacketTunnelCore/Actor/Protocols/SettingsReaderProtocol.swift b/ios/PacketTunnelCore/Actor/Protocols/SettingsReaderProtocol.swift
index 9d75149b45..ffe7cdcc2f 100644
--- a/ios/PacketTunnelCore/Actor/Protocols/SettingsReaderProtocol.swift
+++ b/ios/PacketTunnelCore/Actor/Protocols/SettingsReaderProtocol.swift
@@ -40,18 +40,22 @@ public struct Settings {
/// Obfuscation settings
public var obfuscation: WireGuardObfuscationSettings
+ public var quantumResistance: TunnelQuantumResistance
+
public init(
privateKey: PrivateKey,
interfaceAddresses: [IPAddressRange],
relayConstraints: RelayConstraints,
dnsServers: SelectedDNSServers,
- obfuscation: WireGuardObfuscationSettings
+ obfuscation: WireGuardObfuscationSettings,
+ quantumResistance: TunnelQuantumResistance
) {
self.privateKey = privateKey
self.interfaceAddresses = interfaceAddresses
self.relayConstraints = relayConstraints
self.dnsServers = dnsServers
self.obfuscation = obfuscation
+ self.quantumResistance = quantumResistance
}
}
diff --git a/ios/PacketTunnelCore/Actor/State+Extensions.swift b/ios/PacketTunnelCore/Actor/State+Extensions.swift
index 45b28a0c9c..be1f05d52d 100644
--- a/ios/PacketTunnelCore/Actor/State+Extensions.swift
+++ b/ios/PacketTunnelCore/Actor/State+Extensions.swift
@@ -23,7 +23,7 @@ extension State {
case .initial:
return .connecting
- case .connecting:
+ case .connecting, .negotiatingPostQuantumKey:
return .connecting
case .connected, .reconnecting:
@@ -59,13 +59,15 @@ extension State {
case let .error(blockedState):
return "\(name): \(blockedState.reason)"
- case .initial, .disconnecting, .disconnected:
+ case .initial, .disconnecting, .disconnected, .negotiatingPostQuantumKey:
return name
}
}
var name: String {
switch self {
+ case .negotiatingPostQuantumKey:
+ "Negotiating Post Quantum Key"
case .connected:
"Connected"
case .connecting:
@@ -89,6 +91,7 @@ extension State {
let .connecting(connState),
let .connected(connState),
let .reconnecting(connState),
+ let .negotiatingPostQuantumKey(connState, _),
let .disconnecting(connState): connState
default: nil
}
@@ -118,6 +121,7 @@ extension State {
case .connected: .connected(newValue)
case .reconnecting: .reconnecting(newValue)
case .disconnecting: .disconnecting(newValue)
+ case let .negotiatingPostQuantumKey(_, privateKey): .negotiatingPostQuantumKey(newValue, privateKey)
default: self
}
}
@@ -130,6 +134,7 @@ extension State {
case let .connecting(connState),
let .connected(connState),
let .reconnecting(connState),
+ let .negotiatingPostQuantumKey(connState, _),
let .disconnecting(connState):
var associatedData: StateAssociatedData = connState
modifier(&associatedData)
diff --git a/ios/PacketTunnelCore/Actor/State.swift b/ios/PacketTunnelCore/Actor/State.swift
index ab3fe767c9..f99799201c 100644
--- a/ios/PacketTunnelCore/Actor/State.swift
+++ b/ios/PacketTunnelCore/Actor/State.swift
@@ -58,6 +58,9 @@ enum State: Equatable {
/// Initial state at the time when actor is initialized but before the first connection attempt.
case initial
+ /// Establish a connection to the gateway, and exchange a post quantum key with the GRPC service that resides there.
+ case negotiatingPostQuantumKey(ConnectionData, PrivateKey)
+
/// Tunnel is attempting to connect.
/// The actor should remain in this state until the very first connection is established, i.e determined by tunnel monitor.
case connecting(ConnectionData)
@@ -143,6 +146,9 @@ extension State {
/// The remote port that was chosen to connect to `connectedEndpoint`
public let remotePort: UInt16
+
+ /// True if post-quantum key exchange is enabled
+ public let isPostQuantum: Bool
}
/// Data associated with error state.
diff --git a/ios/PacketTunnelCoreTests/Mocks/SettingsReaderStub.swift b/ios/PacketTunnelCoreTests/Mocks/SettingsReaderStub.swift
index c95133b091..edb9e99e6d 100644
--- a/ios/PacketTunnelCoreTests/Mocks/SettingsReaderStub.swift
+++ b/ios/PacketTunnelCoreTests/Mocks/SettingsReaderStub.swift
@@ -29,7 +29,8 @@ extension SettingsReaderStub {
interfaceAddresses: [IPAddressRange(from: "127.0.0.1/32")!],
relayConstraints: RelayConstraints(),
dnsServers: .gateway,
- obfuscation: WireGuardObfuscationSettings(state: .off, port: .automatic)
+ obfuscation: WireGuardObfuscationSettings(state: .off, port: .automatic),
+ quantumResistance: .automatic
)
return SettingsReaderStub {
diff --git a/ios/PacketTunnelCoreTests/PacketTunnelActorTests.swift b/ios/PacketTunnelCoreTests/PacketTunnelActorTests.swift
index fb37ef6b0d..9fa8b90258 100644
--- a/ios/PacketTunnelCoreTests/PacketTunnelActorTests.swift
+++ b/ios/PacketTunnelCoreTests/PacketTunnelActorTests.swift
@@ -208,7 +208,8 @@ final class PacketTunnelActorTests: XCTestCase {
interfaceAddresses: [IPAddressRange(from: "127.0.0.1/32")!],
relayConstraints: RelayConstraints(),
dnsServers: .gateway,
- obfuscation: WireGuardObfuscationSettings(state: .off, port: .automatic)
+ obfuscation: WireGuardObfuscationSettings(state: .off, port: .automatic),
+ quantumResistance: .automatic
)
}
}
diff --git a/ios/PacketTunnelCoreTests/ProtocolObfuscatorTests.swift b/ios/PacketTunnelCoreTests/ProtocolObfuscatorTests.swift
index dd644a74e1..21f30991bb 100644
--- a/ios/PacketTunnelCoreTests/ProtocolObfuscatorTests.swift
+++ b/ios/PacketTunnelCoreTests/ProtocolObfuscatorTests.swift
@@ -34,14 +34,14 @@ final class ProtocolObfuscatorTests: XCTestCase {
}
func testObfuscateOffDoesNotChangeEndpoint() {
- let settings = settings(.off, obfuscationPort: .automatic)
+ let settings = settings(.off, obfuscationPort: .automatic, quantumResistance: .automatic)
let nonObfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings)
XCTAssertEqual(endpoint, nonObfuscatedEndpoint)
}
func testObfuscateOnPort80() throws {
- let settings = settings(.on, obfuscationPort: .port80)
+ let settings = settings(.on, obfuscationPort: .port80, quantumResistance: .automatic)
let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings)
let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub)
@@ -49,7 +49,7 @@ final class ProtocolObfuscatorTests: XCTestCase {
}
func testObfuscateOnPort5001() throws {
- let settings = settings(.on, obfuscationPort: .port5001)
+ let settings = settings(.on, obfuscationPort: .port5001, quantumResistance: .automatic)
let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings)
let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub)
@@ -57,7 +57,7 @@ final class ProtocolObfuscatorTests: XCTestCase {
}
func testObfuscateOnPortAutomaticIsPort80OnEvenRetryAttempts() throws {
- let settings = settings(.on, obfuscationPort: .automatic)
+ let settings = settings(.on, obfuscationPort: .automatic, quantumResistance: .automatic)
let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings, retryAttempts: 2)
let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub)
@@ -65,7 +65,7 @@ final class ProtocolObfuscatorTests: XCTestCase {
}
func testObfuscateOnPortAutomaticIsPort5001OnOddRetryAttempts() throws {
- let settings = settings(.on, obfuscationPort: .automatic)
+ let settings = settings(.on, obfuscationPort: .automatic, quantumResistance: .automatic)
let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings, retryAttempts: 3)
let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub)
@@ -73,7 +73,7 @@ final class ProtocolObfuscatorTests: XCTestCase {
}
func testObfuscateAutomaticIsPort80EveryThirdAttempts() throws {
- let settings = settings(.automatic, obfuscationPort: .automatic)
+ let settings = settings(.automatic, obfuscationPort: .automatic, quantumResistance: .automatic)
let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings, retryAttempts: 6)
let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub)
@@ -81,7 +81,7 @@ final class ProtocolObfuscatorTests: XCTestCase {
}
func testObfuscateAutomaticIsPort5001EveryFourthAttempts() throws {
- let settings = settings(.automatic, obfuscationPort: .automatic)
+ let settings = settings(.automatic, obfuscationPort: .automatic, quantumResistance: .automatic)
let obfuscatedEndpoint = obfuscator.obfuscate(endpoint, settings: settings, retryAttempts: 7)
let obfuscationProtocol = try XCTUnwrap(obfuscator.tunnelObfuscator as? TunnelObfuscationStub)
@@ -100,7 +100,8 @@ final class ProtocolObfuscatorTests: XCTestCase {
private func settings(
_ obfuscationState: WireGuardObfuscationState,
- obfuscationPort: WireGuardObfuscationPort
+ obfuscationPort: WireGuardObfuscationPort,
+ quantumResistance: TunnelQuantumResistance
) -> Settings {
Settings(
privateKey: PrivateKey(),
@@ -110,7 +111,7 @@ final class ProtocolObfuscatorTests: XCTestCase {
obfuscation: WireGuardObfuscationSettings(
state: obfuscationState,
port: obfuscationPort
- )
+ ), quantumResistance: quantumResistance
)
}
}
diff --git a/ios/build-rust-library.sh b/ios/build-rust-library.sh
index 5de58559b0..94b8571a78 100644
--- a/ios/build-rust-library.sh
+++ b/ios/build-rust-library.sh
@@ -9,6 +9,8 @@ then
exit 1
fi
+
+
# what to pass to cargo build -p, e.g. your_lib_ffi
FFI_TARGET=$1
@@ -30,13 +32,17 @@ if [[ "$CONFIGURATION" == "MockRelease" ]]; then
RELFLAG=--release
fi
-if [[ -n "${DEVELOPER_SDK_DIR:-}" ]]; then
- # Assume we're in Xcode, which means we're probably cross-compiling.
- # In this case, we need to add an extra library search path for build scripts and proc-macros,
- # which run on the host instead of the target.
- # (macOS Big Sur does not have linkable libraries in /usr/lib/.)
- export LIBRARY_PATH="${DEVELOPER_SDK_DIR}/MacOSX.sdk/usr/lib:${LIBRARY_PATH:-}"
-fi
+# For whatever reason, Xcode includes its toolchain paths in the PATH variable such as
+#
+# /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
+# /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/appleinternal/bin
+# /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/local/bin
+# /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/libexec
+# When this happens, cargo will be tricked into building for the wrong architecture, which will lead to linker issues down the line.
+# cargo does not need to know about all this, therefore, set the path to the bare minimum
+export PATH="${HOME}/.cargo/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin:"
+# Since some of the dependencies come from homebrew, add it manually as well
+export PATH="${PATH}:/opt/homebrew/bin:"
IS_SIMULATOR=0
if [ "${LLVM_TARGET_TRIPLE_SUFFIX-}" = "-simulator" ]; then
diff --git a/talpid-tunnel-config-client/Cargo.toml b/talpid-tunnel-config-client/Cargo.toml
index 650f2ba566..cce1aa86b0 100644
--- a/talpid-tunnel-config-client/Cargo.toml
+++ b/talpid-tunnel-config-client/Cargo.toml
@@ -17,20 +17,28 @@ talpid-types = { path = "../talpid-types" }
tonic = { workspace = true }
tower = { workspace = true }
prost = { workspace = true }
-tokio = { workspace = true, features = ["macros"] }
-classic-mceliece-rust = { version = "2.0.0", features = ["mceliece460896f", "zeroize"] }
+tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
+classic-mceliece-rust = { version = "2.0.0", features = [
+ "mceliece460896f",
+ "zeroize",
+] }
pqc_kyber = { version = "0.4.0", features = ["std", "kyber1024", "zeroize"] }
zeroize = "1.5.7"
libc = "0.2"
[target.'cfg(windows)'.dependencies.windows-sys]
workspace = true
-features = [
- "Win32_Networking_WinSock"
-]
-
-[dev-dependencies]
-tokio = { workspace = true, features = ["rt-multi-thread"] }
+features = ["Win32_Networking_WinSock"]
[build-dependencies]
-tonic-build = { workspace = true, default-features = false, features = ["transport", "prost"] }
+tonic-build = { workspace = true, default-features = false, features = [
+ "transport",
+ "prost",
+] }
+cbindgen = { version = "0.24.3", default-features = false }
+
+[target.'cfg(target_os = "ios")'.dependencies]
+oslog = "0.2"
+
+[lib]
+crate-type = ["staticlib", "rlib"]
diff --git a/talpid-tunnel-config-client/build.rs b/talpid-tunnel-config-client/build.rs
index aeb21fe009..50c9bfd1df 100644
--- a/talpid-tunnel-config-client/build.rs
+++ b/talpid-tunnel-config-client/build.rs
@@ -1,3 +1,15 @@
fn main() {
tonic_build::compile_protos("proto/ephemeralpeer.proto").unwrap();
+ match std::env::var("TARGET").unwrap().as_str() {
+ "aarch64-apple-ios" | "aarch64-apple-ios-sim" => {
+ let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
+ cbindgen::Builder::new()
+ .with_crate(crate_dir)
+ .with_language(cbindgen::Language::C)
+ .generate()
+ .expect("failed to generate bindings")
+ .write_to_file("../ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h");
+ }
+ &_ => (),
+ }
}
diff --git a/talpid-tunnel-config-client/src/ios_ffi/ios_runtime.rs b/talpid-tunnel-config-client/src/ios_ffi/ios_runtime.rs
new file mode 100644
index 0000000000..37fdb0d16d
--- /dev/null
+++ b/talpid-tunnel-config-client/src/ios_ffi/ios_runtime.rs
@@ -0,0 +1,168 @@
+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 talpid_types::net::wireguard::{PrivateKey, PublicKey};
+use tokio::{runtime::Builder, sync::mpsc};
+use tonic::transport::channel::Endpoint;
+use tower::util::service_fn;
+
+/// # Safety
+/// packet_tunnel and tcp_connection must be valid pointers to a packet tunnel and a TCP connection
+/// instances.
+pub unsafe fn run_post_quantum_psk_exchange(
+ pub_key: [u8; 32],
+ ephemeral_key: [u8; 32],
+ packet_tunnel: *const c_void,
+ tcp_connection: *const c_void,
+) -> Result<PostQuantumCancelToken, Error> {
+ match unsafe { IOSRuntime::new(pub_key, ephemeral_key, packet_tunnel, tcp_connection) } {
+ Ok(runtime) => {
+ let token = runtime.cancel_token_tx.clone();
+
+ runtime.run();
+ Ok(PostQuantumCancelToken {
+ context: Arc::into_raw(token) as *mut _,
+ })
+ }
+ Err(err) => {
+ log::error!("Failed to create runtime {}", err);
+ Err(Error::UnableToCreateRuntime)
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct SwiftContext {
+ pub packet_tunnel: *const c_void,
+ pub tcp_connection: *const c_void,
+}
+
+unsafe impl Send for SwiftContext {}
+unsafe impl Sync for SwiftContext {}
+
+struct IOSRuntime {
+ runtime: tokio::runtime::Runtime,
+ pub_key: [u8; 32],
+ ephemeral_key: [u8; 32],
+ packet_tunnel: SwiftContext,
+ cancel_token_tx: Arc<mpsc::UnboundedSender<()>>,
+ cancel_token_rx: mpsc::UnboundedReceiver<()>,
+}
+
+impl IOSRuntime {
+ pub unsafe fn new(
+ pub_key: [u8; 32],
+ ephemeral_key: [u8; 32],
+ packet_tunnel: *const libc::c_void,
+ tcp_connection: *const c_void,
+ ) -> io::Result<Self> {
+ let runtime = Builder::new_multi_thread()
+ .enable_all()
+ .worker_threads(2)
+ .build()?;
+
+ let context = SwiftContext {
+ packet_tunnel,
+ 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,
+ })
+ }
+
+ pub fn run(self) {
+ std::thread::spawn(move || {
+ 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`
+ async unsafe fn ios_tcp_client(
+ ctx: SwiftContext,
+ ) -> Result<(RelayConfigService, IosTcpShutdownHandle), Error> {
+ let endpoint = Endpoint::from_static("tcp://0.0.0.0:0");
+
+ let (tcp_provider, conn_handle) = unsafe { IosTcpProvider::new(ctx.tcp_connection) };
+ // One (1) TCP connection
+ let mut one_tcp_connection = Some(tcp_provider);
+ let conn = endpoint
+ .connect_with_connector(service_fn(
+ move |_| -> Pin<Box<dyn Future<Output = _> + Send>> {
+ if let Some(connection) = one_tcp_connection.take() {
+ return Box::pin(async move { Ok::<_, Error>(connection) });
+ }
+ Box::pin(async { Err(Error::TcpConnectionExpired) })
+ },
+ ))
+ .await
+ .map_err(Error::GrpcConnectError)?;
+
+ Ok((RelayConfigService::new(conn), conn_handle))
+ }
+
+ fn run_service_inner(self) {
+ let Self {
+ runtime,
+ mut cancel_token_rx,
+ ..
+ } = self;
+
+ let packet_tunnel_ptr = self.packet_tunnel.packet_tunnel;
+ runtime.block_on(async move {
+ let (async_provider, shutdown_handle) = unsafe { match Self::ios_tcp_client(self.packet_tunnel).await {
+ Ok(result) => result,
+ Err(error) => {
+ log::error!("Failed to create iOS TCP client: {error}");
+ swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null());
+ return;
+ }
+ }};
+ let ephemeral_pub_key = PrivateKey::from(self.ephemeral_key).public_key();
+
+ tokio::select! {
+ ephemeral_peer = request_ephemeral_peer_with(
+ async_provider,
+ PublicKey::from(self.pub_key),
+ ephemeral_pub_key,
+ true,
+ false,
+ ) => {
+ shutdown_handle.shutdown();
+ match ephemeral_peer {
+ Ok(peer) => {
+ match peer.psk {
+ Some(preshared_key) => unsafe {
+ 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());
+ }
+
+ }
+ },
+ Err(_) => 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.
+ }
+ }
+ });
+ }
+}
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
new file mode 100644
index 0000000000..86a3114edf
--- /dev/null
+++ b/talpid-tunnel-config-client/src/ios_ffi/ios_tcp_connection.rs
@@ -0,0 +1,194 @@
+use libc::c_void;
+use std::{
+ io::{self, Result},
+ sync::{
+ atomic::{self, AtomicBool},
+ Arc, Mutex, Weak,
+ },
+ task::{Poll, Waker},
+};
+use tokio::{
+ io::{AsyncRead, AsyncWrite},
+ sync::mpsc,
+};
+
+fn connection_closed_err() -> io::Error {
+ io::Error::new(io::ErrorKind::BrokenPipe, "TCP connection closed")
+}
+
+extern "C" {
+ /// 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.
+ pub fn swift_nw_tcp_connection_send(
+ connection: *const libc::c_void,
+ data: *const libc::c_void,
+ data_len: usize,
+ sender: *const libc::c_void,
+ );
+
+ /// 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.
+ pub fn swift_nw_tcp_connection_read(
+ connection: *const libc::c_void,
+ sender: *const libc::c_void,
+ );
+
+ /// Called when the preshared post quantum key is ready.
+ /// `raw_preshared_key` might be NULL if the key negotiation failed.
+ pub fn swift_post_quantum_key_ready(
+ raw_packet_tunnel: *const c_void,
+ raw_preshared_key: *const u8,
+ raw_ephemeral_private_key: *const u8,
+ );
+}
+
+unsafe impl Send for IosTcpProvider {}
+
+pub struct IosTcpProvider {
+ write_tx: Arc<mpsc::UnboundedSender<usize>>,
+ write_rx: mpsc::UnboundedReceiver<usize>,
+ read_tx: Arc<mpsc::UnboundedSender<Box<[u8]>>>,
+ read_rx: mpsc::UnboundedReceiver<Box<[u8]>>,
+ tcp_connection: *const c_void,
+ 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>>>,
+}
+
+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) {
+ 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 {
+ write_tx: Arc::new(tx),
+ write_rx: rx,
+ read_tx: Arc::new(recv_tx),
+ read_rx: recv_rx,
+ tcp_connection,
+ read_in_progress: false,
+ write_in_progress: false,
+ shutdown: shutdown.clone(),
+ waker: waker.clone(),
+ },
+ IosTcpShutdownHandle { shutdown, waker },
+ )
+ }
+
+ 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);
+ }
+ }
+}
+
+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()) {
+ waker.wake();
+ }
+ }
+}
+
+impl AsyncWrite for IosTcpProvider {
+ fn poll_write(
+ mut self: std::pin::Pin<&mut Self>,
+ cx: &mut std::task::Context<'_>,
+ buf: &[u8],
+ ) -> std::task::Poll<Result<usize>> {
+ self.maybe_set_waker(cx.waker().clone());
+ match self.write_rx.poll_recv(cx) {
+ std::task::Poll::Ready(Some(bytes_sent)) => {
+ self.write_in_progress = false;
+ Poll::Ready(Ok(bytes_sent))
+ }
+ std::task::Poll::Ready(None) => {
+ self.write_in_progress = false;
+ 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,
+ buf.as_ptr() as _,
+ buf.len(),
+ raw_sender as _,
+ );
+ }
+ self.write_in_progress = true;
+ }
+ std::task::Poll::Pending
+ }
+ }
+ }
+
+ fn poll_flush(
+ self: std::pin::Pin<&mut Self>,
+ _: &mut std::task::Context<'_>,
+ ) -> std::task::Poll<Result<()>> {
+ std::task::Poll::Ready(Ok(()))
+ }
+
+ fn poll_shutdown(
+ self: std::pin::Pin<&mut Self>,
+ _: &mut std::task::Context<'_>,
+ ) -> std::task::Poll<Result<()>> {
+ std::task::Poll::Ready(Ok(()))
+ }
+}
+impl AsyncRead for IosTcpProvider {
+ fn poll_read(
+ mut self: std::pin::Pin<&mut Self>,
+ cx: &mut std::task::Context<'_>,
+ buf: &mut tokio::io::ReadBuf<'_>,
+ ) -> std::task::Poll<std::io::Result<()>> {
+ if self.is_shutdown() {
+ return Poll::Ready(Err(connection_closed_err()));
+ }
+
+ match self.read_rx.poll_recv(cx) {
+ std::task::Poll::Ready(Some(data)) => {
+ buf.put_slice(&data);
+ self.read_in_progress = false;
+ Poll::Ready(Ok(()))
+ }
+ std::task::Poll::Ready(None) => {
+ self.read_in_progress = false;
+ Poll::Ready(Err(connection_closed_err()))
+ }
+ std::task::Poll::Pending => {
+ 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 _);
+ }
+ self.read_in_progress = true;
+ }
+ Poll::Pending
+ }
+ }
+ }
+}
diff --git a/talpid-tunnel-config-client/src/ios_ffi/mod.rs b/talpid-tunnel-config-client/src/ios_ffi/mod.rs
new file mode 100644
index 0000000000..94dcf95b6f
--- /dev/null
+++ b/talpid-tunnel-config-client/src/ios_ffi/mod.rs
@@ -0,0 +1,134 @@
+pub mod ios_runtime;
+pub mod ios_tcp_connection;
+
+use crate::ios_ffi::ios_runtime::run_post_quantum_psk_exchange;
+use libc::c_void;
+use std::sync::{Arc, Weak};
+use tokio::sync::mpsc;
+
+use std::sync::Once;
+static INIT_LOGGING: Once = Once::new();
+
+#[repr(C)]
+pub struct PostQuantumCancelToken {
+ // Must keep a pointer to a valid std::sync::Arc<tokio::mpsc::UnboundedSender>
+ pub context: *mut c_void,
+}
+
+impl PostQuantumCancelToken {
+ /// # Safety
+ /// 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(());
+ // Call std::mem::forget here to avoid dropping the channel.
+ std::mem::forget(send_tx);
+ }
+}
+
+impl Drop for PostQuantumCancelToken {
+ fn drop(&mut self) {
+ let _: Arc<mpsc::UnboundedSender<()>> = 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`.
+#[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.
+///
+/// # Safety
+/// `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,
+) {
+ let _sender = unsafe { std::ptr::read(sender) };
+}
+
+/// Called by Swift whenever data has been written to the in-tunnel TCP connection when exchanging
+/// quantum-resistant pre shared keys.
+///
+/// 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.
+#[no_mangle]
+pub unsafe extern "C" fn handle_sent(bytes_sent: usize, sender: *const c_void) {
+ let weak_tx: Weak<mpsc::UnboundedSender<usize>> = unsafe { Weak::from_raw(sender as _) };
+ if let Some(send_tx) = weak_tx.upgrade() {
+ _ = send_tx.send(bytes_sent);
+ }
+}
+
+/// 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.
+///
+/// # 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.
+#[no_mangle]
+pub unsafe extern "C" fn handle_recv(data: *const u8, mut data_len: usize, sender: *const c_void) {
+ let weak_tx: Weak<mpsc::UnboundedSender<Box<[u8]>>> = unsafe { Weak::from_raw(sender as _) };
+
+ if data.is_null() {
+ data_len = 0;
+ }
+ let mut bytes = vec![0u8; data_len];
+ if !data.is_null() {
+ std::ptr::copy_nonoverlapping(data, bytes.as_mut_ptr(), data_len);
+ }
+ if let Some(read_tx) = weak_tx.upgrade() {
+ _ = read_tx.send(bytes.into_boxed_slice());
+ }
+}
+
+/// Entry point for exchanging post quantum keys on iOS.
+/// The TCP connection must be created to go through the tunnel.
+/// # 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.
+#[no_mangle]
+pub unsafe extern "C" fn negotiate_post_quantum_key(
+ public_key: *const u8,
+ ephemeral_key: *const u8,
+ packet_tunnel: *const c_void,
+ tcp_connection: *const c_void,
+ cancel_token: *mut PostQuantumCancelToken,
+) -> i32 {
+ INIT_LOGGING.call_once(|| {
+ let _ = oslog::OsLogger::new("net.mullvad.MullvadVPN.TTCC")
+ .level_filter(log::LevelFilter::Trace)
+ .init();
+ });
+
+ let pub_key_copy: [u8; 32] = unsafe { std::ptr::read(public_key as *const [u8; 32]) };
+ 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)
+ } {
+ Ok(token) => {
+ unsafe { std::ptr::write(cancel_token, token) };
+ 0
+ }
+ Err(_) => -1,
+ }
+}
diff --git a/talpid-tunnel-config-client/src/lib.rs b/talpid-tunnel-config-client/src/lib.rs
index e272e794c7..62c124dd44 100644
--- a/talpid-tunnel-config-client/src/lib.rs
+++ b/talpid-tunnel-config-client/src/lib.rs
@@ -1,10 +1,16 @@
-use std::{
- fmt,
- net::{IpAddr, SocketAddr},
-};
+use proto::PostQuantumRequestV1;
+use std::fmt;
+#[cfg(not(target_os = "ios"))]
+use std::net::IpAddr;
+#[cfg(not(target_os = "ios"))]
+use std::net::SocketAddr;
use talpid_types::net::wireguard::{PresharedKey, PublicKey};
+#[cfg(not(target_os = "ios"))]
use tokio::net::TcpSocket;
-use tonic::transport::{Channel, Endpoint};
+use tonic::transport::Channel;
+#[cfg(not(target_os = "ios"))]
+use tonic::transport::Endpoint;
+#[cfg(not(target_os = "ios"))]
use tower::service_fn;
use zeroize::Zeroize;
@@ -16,18 +22,24 @@ mod proto {
tonic::include_proto!("ephemeralpeer");
}
+#[cfg(target_os = "ios")]
+pub mod ios_ffi;
+
+#[cfg(not(target_os = "ios"))]
use libc::setsockopt;
-#[cfg(not(target_os = "windows"))]
+#[cfg(not(any(target_os = "windows", target_os = "ios")))]
mod sys {
pub use libc::{socklen_t, IPPROTO_TCP, TCP_MAXSEG};
pub use std::os::fd::{AsRawFd, RawFd};
}
+
#[cfg(target_os = "windows")]
mod sys {
pub use std::os::windows::io::{AsRawSocket, RawSocket};
pub use windows_sys::Win32::Networking::WinSock::{IPPROTO_IP, IP_USER_MTU};
}
+#[cfg(not(target_os = "ios"))]
use sys::*;
#[derive(Debug)]
@@ -44,6 +56,10 @@ pub enum Error {
actual: usize,
},
FailedDecapsulateKyber(kyber::KyberError),
+ #[cfg(target_os = "ios")]
+ TcpConnectionExpired,
+ #[cfg(target_os = "ios")]
+ UnableToCreateRuntime,
}
impl std::fmt::Display for Error {
@@ -65,6 +81,10 @@ impl std::fmt::Display for Error {
write!(f, "Expected 2 ciphertext in the response, got {actual}")
}
FailedDecapsulateKyber(_) => "Failed to decapsulate Kyber1024 ciphertext".fmt(f),
+ #[cfg(target_os = "ios")]
+ TcpConnectionExpired => "TCP connection is already shut down".fmt(f),
+ #[cfg(target_os = "ios")]
+ UnableToCreateRuntime => "Unable to create iOS PQ PSK runtime".fmt(f),
}
}
}
@@ -79,7 +99,7 @@ impl std::error::Error for Error {
}
}
-type RelayConfigService = proto::ephemeral_peer_client::EphemeralPeerClient<Channel>;
+pub type RelayConfigService = proto::ephemeral_peer_client::EphemeralPeerClient<Channel>;
/// Port used by the tunnel config service.
pub const CONFIG_SERVICE_PORT: u16 = 1337;
@@ -93,48 +113,25 @@ pub const CONFIG_SERVICE_PORT: u16 = 1337;
/// 2. MH + PQ on macOS has connection issues during the handshake due to PF blocking packet
/// fragments for not having a port. In the longer term this might be fixed by allowing the
/// handshake to work even if there is fragmentation.
+#[cfg(not(target_os = "ios"))]
const CONFIG_CLIENT_MTU: u16 = 576;
pub struct EphemeralPeer {
pub psk: Option<PresharedKey>,
}
-/// Negotiate a short-lived peer with a PQ-safe PSK or with DAITA enabled.
-pub async fn request_ephemeral_peer(
- service_address: IpAddr,
+pub async fn request_ephemeral_peer_with(
+ mut client: RelayConfigService,
parent_pubkey: PublicKey,
ephemeral_pubkey: PublicKey,
enable_post_quantum: bool,
enable_daita: bool,
) -> Result<EphemeralPeer, Error> {
- let (pq_request, kem_secrets) = if enable_post_quantum {
- let (cme_kem_pubkey, cme_kem_secret) = classic_mceliece::generate_keys().await;
- let kyber_keypair = kyber::keypair(&mut rand::thread_rng());
-
- (
- Some(proto::PostQuantumRequestV1 {
- kem_pubkeys: vec![
- proto::KemPubkeyV1 {
- algorithm_name: classic_mceliece::ALGORITHM_NAME.to_owned(),
- key_data: cme_kem_pubkey.as_array().to_vec(),
- },
- proto::KemPubkeyV1 {
- algorithm_name: kyber::ALGORITHM_NAME.to_owned(),
- key_data: kyber_keypair.public.to_vec(),
- },
- ],
- }),
- Some((cme_kem_secret, kyber_keypair.secret)),
- )
- } else {
- (None, None)
- };
-
+ let (pq_request, kem_secrets) = post_quantum_secrets(enable_post_quantum).await;
let daita = Some(proto::DaitaRequestV1 {
activate_daita: enable_daita,
});
- let mut client = new_client(service_address).await?;
let response = client
.register_peer_v1(proto::EphemeralPeerRequestV1 {
wg_parent_pubkey: parent_pubkey.as_bytes().to_vec(),
@@ -192,6 +189,57 @@ pub async fn request_ephemeral_peer(
Ok(EphemeralPeer { psk })
}
+/// Negotiate a short-lived peer with a PQ-safe PSK or with DAITA enabled.
+#[cfg(not(target_os = "ios"))]
+pub async fn request_ephemeral_peer(
+ service_address: IpAddr,
+ parent_pubkey: PublicKey,
+ ephemeral_pubkey: PublicKey,
+ enable_post_quantum: bool,
+ enable_daita: bool,
+) -> Result<EphemeralPeer, Error> {
+ let client = new_client(service_address).await?;
+
+ request_ephemeral_peer_with(
+ client,
+ parent_pubkey,
+ ephemeral_pubkey,
+ enable_post_quantum,
+ enable_daita,
+ )
+ .await
+}
+
+async fn post_quantum_secrets(
+ enable_post_quantum: bool,
+) -> (
+ Option<PostQuantumRequestV1>,
+ Option<(classic_mceliece_rust::SecretKey<'static>, [u8; 3168])>,
+) {
+ if enable_post_quantum {
+ let (cme_kem_pubkey, cme_kem_secret) = classic_mceliece::generate_keys().await;
+ let kyber_keypair = kyber::keypair(&mut rand::thread_rng());
+
+ (
+ Some(proto::PostQuantumRequestV1 {
+ kem_pubkeys: vec![
+ proto::KemPubkeyV1 {
+ algorithm_name: classic_mceliece::ALGORITHM_NAME.to_owned(),
+ key_data: cme_kem_pubkey.as_array().to_vec(),
+ },
+ proto::KemPubkeyV1 {
+ algorithm_name: kyber::ALGORITHM_NAME.to_owned(),
+ key_data: kyber_keypair.public.to_vec(),
+ },
+ ],
+ }),
+ Some((cme_kem_secret, kyber_keypair.secret)),
+ )
+ } else {
+ (None, None)
+ }
+}
+
/// Performs `dst = dst ^ src`.
fn xor_assign(dst: &mut [u8; 32], src: &[u8; 32]) {
for (dst_byte, src_byte) in dst.iter_mut().zip(src.iter()) {
@@ -199,6 +247,7 @@ fn xor_assign(dst: &mut [u8; 32], src: &[u8; 32]) {
}
}
+#[cfg(not(target_os = "ios"))]
async fn new_client(addr: IpAddr) -> Result<RelayConfigService, Error> {
let endpoint = Endpoint::from_static("tcp://0.0.0.0:0");
@@ -245,7 +294,7 @@ fn try_set_tcp_sock_mtu(sock: RawSocket, mtu: u16) {
}
}
-#[cfg(not(windows))]
+#[cfg(not(any(target_os = "windows", target_os = "ios")))]
fn try_set_tcp_sock_mtu(dest: &IpAddr, sock: RawFd, mut mtu: u16) {
const IPV4_HEADER_SIZE: u16 = 20;
const IPV6_HEADER_SIZE: u16 = 40;