summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadPostQuantum
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 /ios/MullvadPostQuantum
parentcba171ca2232838b3e7628221ed1b5a56f93230f (diff)
parent0c682760f4a7f8b2852f3212c98214b11c916661 (diff)
downloadmullvadvpn-2211f2875fc7953eea2ec2caa04f03d91f58be49.tar.xz
mullvadvpn-2211f2875fc7953eea2ec2caa04f03d91f58be49.zip
Merge remote-tracking branch 'origin/features/ios-post-quantum'
Diffstat (limited to 'ios/MullvadPostQuantum')
-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
5 files changed, 281 insertions, 0 deletions
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);