diff options
| author | Emīls <emils@mullvad.net> | 2024-05-21 17:04:43 +0200 |
|---|---|---|
| committer | Emīls <emils@mullvad.net> | 2024-05-21 17:04:43 +0200 |
| commit | 2211f2875fc7953eea2ec2caa04f03d91f58be49 (patch) | |
| tree | 740bb2e438864e0f273bc724658048170a8416fd /ios/MullvadPostQuantum | |
| parent | cba171ca2232838b3e7628221ed1b5a56f93230f (diff) | |
| parent | 0c682760f4a7f8b2852f3212c98214b11c916661 (diff) | |
| download | mullvadvpn-2211f2875fc7953eea2ec2caa04f03d91f58be49.tar.xz mullvadvpn-2211f2875fc7953eea2ec2caa04f03d91f58be49.zip | |
Merge remote-tracking branch 'origin/features/ios-post-quantum'
Diffstat (limited to 'ios/MullvadPostQuantum')
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); |
