diff --git a/ios/MullvadRustRuntime/EphemeralPeerReceiver.swift b/ios/MullvadRustRuntime/EphemeralPeerReceiver.swift index 2b1b4adac53b..6752ec3e51fc 100644 --- a/ios/MullvadRustRuntime/EphemeralPeerReceiver.swift +++ b/ios/MullvadRustRuntime/EphemeralPeerReceiver.swift @@ -24,11 +24,13 @@ import WireGuardKitTypes /// - rawEphemeralPeerReceiver: A raw pointer to the running instance of `NEPacketTunnelProvider` /// - rawPresharedKey: A raw pointer to the quantum-secure pre shared key /// - rawEphemeralKey: A raw pointer to the ephemeral private key of the device -@_cdecl("swift_ephemeral_peer_ready") +/// - rawDaitaParameters: A raw pointer to negotiated DAITA parameters +@_silgen_name("swift_ephemeral_peer_ready") func receivePostQuantumKey( rawEphemeralPeerReceiver: UnsafeMutableRawPointer?, rawPresharedKey: UnsafeMutableRawPointer?, - rawEphemeralKey: UnsafeMutableRawPointer? + rawEphemeralKey: UnsafeMutableRawPointer?, + rawDaitaParameters: UnsafePointer? ) { guard let rawEphemeralPeerReceiver else { return } let ephemeralPeerReceiver = Unmanaged.fromOpaque(rawEphemeralPeerReceiver) @@ -41,12 +43,29 @@ func receivePostQuantumKey( return } + let maybeNot = Maybenot() + let daitaParameters: DaitaV2Parameters? = rawDaitaParameters?.withMemoryRebound( + to: DaitaParameters.self, + capacity: 1 + ) { body in + let params = body.pointee + guard params.machines != nil else { return nil } + let machines = String(cString: params.machines) + return DaitaV2Parameters( + machines: machines, + maximumEvents: maybeNot.maximumEvents, + maximumActions: maybeNot.maximumActions, + maximumPadding: params.max_padding_frac, + maximumBlocking: params.max_blocking_frac + ) + } + // If there is a pre-shared key, an ephemeral peer was negotiated with Post Quantum options // Otherwise, a Daita enabled ephemeral peer was requested if let rawPresharedKey, let key = PreSharedKey(rawValue: Data(bytes: rawPresharedKey, count: 32)) { - ephemeralPeerReceiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey) + ephemeralPeerReceiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey, daitaParameters: daitaParameters) } else { - ephemeralPeerReceiver.receiveEphemeralPeerPrivateKey(ephemeralKey) + ephemeralPeerReceiver.receiveEphemeralPeerPrivateKey(ephemeralKey, daitaParameters: daitaParameters) } return } diff --git a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h index 93c04587f16a..ed79558f5b10 100644 --- a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h +++ b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h @@ -27,6 +27,12 @@ typedef struct ProxyHandle { uint16_t port; } ProxyHandle; +typedef struct DaitaParameters { + uint8_t *machines; + double max_padding_frac; + double max_blocking_frac; +} DaitaParameters; + typedef struct WgTcpConnectionFunctions { int32_t (*open_fn)(int32_t tunnelHandle, const char *address, uint64_t timeout); int32_t (*close_fn)(int32_t tunnelHandle, int32_t socketHandle); @@ -88,6 +94,18 @@ int32_t encrypted_dns_proxy_start(struct EncryptedDnsProxyState *encrypted_dns_p */ int32_t encrypted_dns_proxy_stop(struct ProxyHandle *proxy_config); +/** + * Called when the preshared post quantum key is ready, + * or when a Daita peer has been successfully requested. + * `raw_preshared_key` will be NULL if: + * - The post quantum key negotiation failed + * - A Daita peer has been requested without enabling post quantum keys. + */ +extern void swift_ephemeral_peer_ready(const void *raw_packet_tunnel, + const uint8_t *raw_preshared_key, + const uint8_t *raw_ephemeral_private_key, + const struct DaitaParameters *daita_parameters); + /** * Called by the Swift side to signal that the ephemeral peer exchange should be cancelled. * After this call, the cancel token is no longer valid. @@ -124,17 +142,6 @@ struct ExchangeCancelToken *request_ephemeral_peer(const uint8_t *public_key, int32_t tunnel_handle, struct EphemeralPeerParameters peer_parameters); -/** - * Called when the preshared post quantum key is ready, - * or when a Daita peer has been successfully requested. - * `raw_preshared_key` will be NULL if: - * - The post quantum key negotiation failed - * - A Daita peer has been requested without enabling post quantum keys. - */ -extern void swift_ephemeral_peer_ready(const void *raw_packet_tunnel, - const uint8_t *raw_preshared_key, - const uint8_t *raw_ephemeral_private_key); - /** * # Safety * `addr`, `password`, `cipher` must be valid for the lifetime of this function call and they must diff --git a/ios/MullvadTypes/Protocols/DaitaV2Parameters.swift b/ios/MullvadTypes/Protocols/DaitaV2Parameters.swift new file mode 100644 index 000000000000..53ab61ada42f --- /dev/null +++ b/ios/MullvadTypes/Protocols/DaitaV2Parameters.swift @@ -0,0 +1,31 @@ +// +// DaitaV2Parameters.swift +// MullvadTypes +// +// Created by Marco Nikic on 2024-11-12. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import Foundation + +public struct DaitaV2Parameters: Equatable { + public let machines: String + public let maximumEvents: UInt32 + public let maximumActions: UInt32 + public let maximumPadding: Double + public let maximumBlocking: Double + + public init( + machines: String, + maximumEvents: UInt32, + maximumActions: UInt32, + maximumPadding: Double, + maximumBlocking: Double + ) { + self.machines = machines + self.maximumEvents = maximumEvents + self.maximumActions = maximumActions + self.maximumPadding = maximumPadding + self.maximumBlocking = maximumBlocking + } +} diff --git a/ios/MullvadTypes/Protocols/EphemeralPeerReceiver.swift b/ios/MullvadTypes/Protocols/EphemeralPeerReceiver.swift index 7f4566946802..91519c746cdc 100644 --- a/ios/MullvadTypes/Protocols/EphemeralPeerReceiver.swift +++ b/ios/MullvadTypes/Protocols/EphemeralPeerReceiver.swift @@ -29,19 +29,26 @@ public class EphemeralPeerReceiver: EphemeralPeerReceiving, TunnelProvider { // MARK: - EphemeralPeerReceiving - public func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) { + public func receivePostQuantumKey( + _ key: PreSharedKey, + ephemeralKey: PrivateKey, + daitaParameters: DaitaV2Parameters? + ) { let semaphore = DispatchSemaphore(value: 0) Task { - await keyReceiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey) + await keyReceiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey, daitaParameters: daitaParameters) semaphore.signal() } semaphore.wait() } - public func receiveEphemeralPeerPrivateKey(_ ephemeralPeerPrivateKey: PrivateKey) { + public func receiveEphemeralPeerPrivateKey( + _ ephemeralPeerPrivateKey: PrivateKey, + daitaParameters: DaitaV2Parameters? + ) { let semaphore = DispatchSemaphore(value: 0) Task { - await keyReceiver.receiveEphemeralPeerPrivateKey(ephemeralPeerPrivateKey) + await keyReceiver.receiveEphemeralPeerPrivateKey(ephemeralPeerPrivateKey, daitaParameters: daitaParameters) semaphore.signal() } semaphore.wait() diff --git a/ios/MullvadTypes/Protocols/EphemeralPeerReceiving.swift b/ios/MullvadTypes/Protocols/EphemeralPeerReceiving.swift index cc1c8f0f8aae..d799dd646c35 100644 --- a/ios/MullvadTypes/Protocols/EphemeralPeerReceiving.swift +++ b/ios/MullvadTypes/Protocols/EphemeralPeerReceiving.swift @@ -15,11 +15,14 @@ public protocol EphemeralPeerReceiving { /// - Parameters: /// - key: The preshared key used by the Ephemeral Peer /// - ephemeralKey: The private key used by the Ephemeral Peer - func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) async + /// - daitaParameters: DAITA parameters + func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey, daitaParameters: DaitaV2Parameters?) async /// Called when successfully requesting an ephemeral peer with Daita enabled, and Post Quantum PSK disabled - /// - Parameter _:_ The private key used by the Ephemeral Peer - func receiveEphemeralPeerPrivateKey(_: PrivateKey) async + /// - Parameters: + /// - _: The private key used by the Ephemeral Peer + /// - daitaParameters: DAITA parameters + func receiveEphemeralPeerPrivateKey(_: PrivateKey, daitaParameters: DaitaV2Parameters?) async /// Called when an ephemeral peer could not be successfully negotiated func ephemeralPeerExchangeFailed() diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift index 75678417ef51..3e61db4cf7f3 100644 --- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift @@ -376,12 +376,12 @@ extension PacketTunnelProvider { } extension PacketTunnelProvider: EphemeralPeerReceiving { - func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) async { - await ephemeralPeerExchangingPipeline.receivePostQuantumKey(key, ephemeralKey: ephemeralKey) + func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey, daitaParameters: MullvadTypes.DaitaV2Parameters?) async { + await ephemeralPeerExchangingPipeline.receivePostQuantumKey(key, ephemeralKey: ephemeralKey, daitaParameters: daitaParameters) } - public func receiveEphemeralPeerPrivateKey(_ ephemeralPeerPrivateKey: PrivateKey) async { - await ephemeralPeerExchangingPipeline.receiveEphemeralPeerPrivateKey(ephemeralPeerPrivateKey) + public func receiveEphemeralPeerPrivateKey(_ ephemeralPeerPrivateKey: PrivateKey, daitaParameters: MullvadTypes.DaitaV2Parameters?) async { + await ephemeralPeerExchangingPipeline.receiveEphemeralPeerPrivateKey(ephemeralPeerPrivateKey, daitaParameters: daitaParameters) } func ephemeralPeerExchangeFailed() { diff --git a/ios/PacketTunnel/PostQuantum/EphemeralPeerExchangingPipeline.swift b/ios/PacketTunnel/PostQuantum/EphemeralPeerExchangingPipeline.swift index 1619889003ab..57cc5baf10f3 100644 --- a/ios/PacketTunnel/PostQuantum/EphemeralPeerExchangingPipeline.swift +++ b/ios/PacketTunnel/PostQuantum/EphemeralPeerExchangingPipeline.swift @@ -8,6 +8,7 @@ import MullvadRustRuntime import MullvadSettings +import MullvadTypes import PacketTunnelCore import WireGuardKitTypes @@ -59,11 +60,18 @@ final public class EphemeralPeerExchangingPipeline { await ephemeralPeerExchanger.start() } - public func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) async { - await ephemeralPeerExchanger.receivePostQuantumKey(key, ephemeralKey: ephemeralKey) + public func receivePostQuantumKey( + _ key: PreSharedKey, + ephemeralKey: PrivateKey, + daitaParameters: DaitaV2Parameters? + ) async { + await ephemeralPeerExchanger.receivePostQuantumKey(key, ephemeralKey: ephemeralKey, daitaParameters: daitaParameters) } - public func receiveEphemeralPeerPrivateKey(_ ephemeralPeerPrivateKey: PrivateKey) async { - await ephemeralPeerExchanger.receiveEphemeralPeerPrivateKey(ephemeralPeerPrivateKey) + public func receiveEphemeralPeerPrivateKey( + _ ephemeralPeerPrivateKey: PrivateKey, + daitaParameters: DaitaV2Parameters? + ) async { + await ephemeralPeerExchanger.receiveEphemeralPeerPrivateKey(ephemeralPeerPrivateKey, daitaParameters: daitaParameters) } } diff --git a/ios/PacketTunnel/PostQuantum/MultiHopEphemeralPeerExchanger.swift b/ios/PacketTunnel/PostQuantum/MultiHopEphemeralPeerExchanger.swift index 0e2bc97778a1..91e3db3bda0b 100644 --- a/ios/PacketTunnel/PostQuantum/MultiHopEphemeralPeerExchanger.swift +++ b/ios/PacketTunnel/PostQuantum/MultiHopEphemeralPeerExchanger.swift @@ -25,6 +25,7 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol { private var entryPeerKey: EphemeralPeerKey! private var exitPeerKey: EphemeralPeerKey! + private var daitaParameters: DaitaV2Parameters? private let defaultGatewayAddressRange = [IPAddressRange(from: "\(LocalNetworkIPs.gatewayAddress.rawValue)/32")!] private let allTrafficRange = [ @@ -66,7 +67,11 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol { await negotiateWithEntry() } - public func receiveEphemeralPeerPrivateKey(_ ephemeralPeerPrivateKey: PrivateKey) async { + public func receiveEphemeralPeerPrivateKey( + _ ephemeralPeerPrivateKey: PrivateKey, + daitaParameters: DaitaV2Parameters? + ) async { + self.daitaParameters = daitaParameters if state == .negotiatingWithEntry { entryPeerKey = EphemeralPeerKey(ephemeralKey: ephemeralPeerPrivateKey) await negotiateBetweenEntryAndExit() @@ -78,8 +83,10 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol { func receivePostQuantumKey( _ preSharedKey: PreSharedKey, - ephemeralKey: PrivateKey + ephemeralKey: PrivateKey, + daitaParameters: DaitaV2Parameters? ) async { + self.daitaParameters = daitaParameters if state == .negotiatingWithEntry { entryPeerKey = EphemeralPeerKey(preSharedKey: preSharedKey, ephemeralKey: ephemeralKey) await negotiateBetweenEntryAndExit() @@ -95,7 +102,8 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol { relay: entry, configuration: EphemeralPeerConfiguration( privateKey: devicePrivateKey, - allowedIPs: defaultGatewayAddressRange + allowedIPs: defaultGatewayAddressRange, + daitaParameters: daitaParameters ) ))) keyExchanger.startNegotiation( @@ -113,14 +121,16 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol { configuration: EphemeralPeerConfiguration( privateKey: entryPeerKey.ephemeralKey, preSharedKey: entryPeerKey.preSharedKey, - allowedIPs: [IPAddressRange(from: "\(exit.endpoint.ipv4Relay.ip)/32")!] + allowedIPs: [IPAddressRange(from: "\(exit.endpoint.ipv4Relay.ip)/32")!], + daitaParameters: self.daitaParameters ) ), exit: EphemeralPeerRelayConfiguration( relay: exit, configuration: EphemeralPeerConfiguration( privateKey: devicePrivateKey, - allowedIPs: defaultGatewayAddressRange + allowedIPs: defaultGatewayAddressRange, + daitaParameters: self.daitaParameters ) ) )) @@ -140,7 +150,8 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol { configuration: EphemeralPeerConfiguration( privateKey: entryPeerKey.ephemeralKey, preSharedKey: entryPeerKey.preSharedKey, - allowedIPs: [IPAddressRange(from: "\(exit.endpoint.ipv4Relay.ip)/32")!] + allowedIPs: [IPAddressRange(from: "\(exit.endpoint.ipv4Relay.ip)/32")!], + daitaParameters: self.daitaParameters ) ), exit: EphemeralPeerRelayConfiguration( @@ -148,7 +159,8 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol { configuration: EphemeralPeerConfiguration( privateKey: exitPeerKey.ephemeralKey, preSharedKey: exitPeerKey.preSharedKey, - allowedIPs: allTrafficRange + allowedIPs: allTrafficRange, + daitaParameters: self.daitaParameters ) ) )) diff --git a/ios/PacketTunnel/PostQuantum/SingleHopEphemeralPeerExchanger.swift b/ios/PacketTunnel/PostQuantum/SingleHopEphemeralPeerExchanger.swift index 3c6ab5631ac4..fc9a08f98978 100644 --- a/ios/PacketTunnel/PostQuantum/SingleHopEphemeralPeerExchanger.swift +++ b/ios/PacketTunnel/PostQuantum/SingleHopEphemeralPeerExchanger.swift @@ -45,7 +45,8 @@ struct SingleHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol { relay: exit, configuration: EphemeralPeerConfiguration( privateKey: devicePrivateKey, - allowedIPs: [IPAddressRange(from: "\(LocalNetworkIPs.gatewayAddress.rawValue)/32")!] + allowedIPs: [IPAddressRange(from: "\(LocalNetworkIPs.gatewayAddress.rawValue)/32")!], + daitaParameters: nil ) ))) keyExchanger.startNegotiation( @@ -55,7 +56,7 @@ struct SingleHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol { ) } - public func receiveEphemeralPeerPrivateKey(_ ephemeralKey: PrivateKey) async { + public func receiveEphemeralPeerPrivateKey(_ ephemeralKey: PrivateKey, daitaParameters: DaitaV2Parameters?) async { await onUpdateConfiguration(.single(EphemeralPeerRelayConfiguration( relay: exit, configuration: EphemeralPeerConfiguration( @@ -64,7 +65,8 @@ struct SingleHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol { allowedIPs: [ IPAddressRange(from: "\(LocalNetworkIPs.defaultRouteIpV4.rawValue)/0")!, IPAddressRange(from: "\(LocalNetworkIPs.defaultRouteIpV6.rawValue)/0")!, - ] + ], + daitaParameters: daitaParameters ) ))) self.onFinish() @@ -72,7 +74,8 @@ struct SingleHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol { func receivePostQuantumKey( _ preSharedKey: WireGuardKitTypes.PreSharedKey, - ephemeralKey: WireGuardKitTypes.PrivateKey + ephemeralKey: WireGuardKitTypes.PrivateKey, + daitaParameters: DaitaV2Parameters? ) async { await onUpdateConfiguration(.single(EphemeralPeerRelayConfiguration( relay: exit, @@ -82,7 +85,8 @@ struct SingleHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol { allowedIPs: [ IPAddressRange(from: "\(LocalNetworkIPs.defaultRouteIpV4.rawValue)/0")!, IPAddressRange(from: "\(LocalNetworkIPs.defaultRouteIpV6.rawValue)/0")!, - ] + ], + daitaParameters: daitaParameters ) ))) self.onFinish() diff --git a/ios/PacketTunnelCore/Actor/EphemeralPeerNegotiationState.swift b/ios/PacketTunnelCore/Actor/EphemeralPeerNegotiationState.swift index 339e458ce266..18eee61f3b0a 100644 --- a/ios/PacketTunnelCore/Actor/EphemeralPeerNegotiationState.swift +++ b/ios/PacketTunnelCore/Actor/EphemeralPeerNegotiationState.swift @@ -7,6 +7,7 @@ // import MullvadREST +import MullvadTypes import WireGuardKitTypes public enum EphemeralPeerNegotiationState: Equatable { @@ -43,11 +44,18 @@ public struct EphemeralPeerConfiguration: Equatable, CustomDebugStringConvertibl public let privateKey: PrivateKey public let preSharedKey: PreSharedKey? public let allowedIPs: [IPAddressRange] + public let daitaParameters: DaitaV2Parameters? - public init(privateKey: PrivateKey, preSharedKey: PreSharedKey? = nil, allowedIPs: [IPAddressRange]) { + public init( + privateKey: PrivateKey, + preSharedKey: PreSharedKey? = nil, + allowedIPs: [IPAddressRange], + daitaParameters: DaitaV2Parameters? + ) { self.privateKey = privateKey self.preSharedKey = preSharedKey self.allowedIPs = allowedIPs + self.daitaParameters = daitaParameters } public var debugDescription: String { diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor+PostQuantum.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor+PostQuantum.swift index f35677c84622..ced44e3a3d09 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor+PostQuantum.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor+PostQuantum.swift @@ -7,6 +7,7 @@ // import Foundation +import MullvadTypes import WireGuardKitTypes extension PacketTunnelActor { @@ -65,14 +66,6 @@ extension PacketTunnelActor { } var daitaConfiguration: DaitaConfiguration? - if settings.daita.daitaState.isEnabled { - let maybeNot = Maybenot() - daitaConfiguration = DaitaConfiguration( - machines: maybeNot.machines, - maxEvents: maybeNot.maximumEvents, - maxActions: maybeNot.maximumActions - ) - } switch configuration { case let .single(hop): @@ -81,6 +74,9 @@ extension PacketTunnelActor { settings: settings, connectionData: connectionData ).make().exitConfiguration + if settings.daita.daitaState.isEnabled, let daitaSettings = hop.configuration.daitaParameters { + daitaConfiguration = DaitaConfiguration(daita: daitaSettings) + } try await tunnelAdapter.start(configuration: exitConfiguration, daita: daitaConfiguration) case let .multi(firstHop, secondHop): @@ -90,6 +86,10 @@ extension PacketTunnelActor { connectionData: connectionData ).make() + if settings.daita.daitaState.isEnabled, let daitaSettings = firstHop.configuration.daitaParameters { + daitaConfiguration = DaitaConfiguration(daita: daitaSettings) + } + try await tunnelAdapter.startMultihop( entryConfiguration: connectionConfiguration.entryConfiguration, exitConfiguration: connectionConfiguration.exitConfiguration, daita: daitaConfiguration @@ -97,3 +97,15 @@ extension PacketTunnelActor { } } } + +extension DaitaConfiguration { + init(daita: DaitaV2Parameters) { + self = DaitaConfiguration( + machines: daita.machines, + maxEvents: daita.maximumEvents, + maxActions: daita.maximumActions, + maxPadding: daita.maximumPadding, + maxBlocking: daita.maximumBlocking + ) + } +} diff --git a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift index d9cce79f576d..469bf0fa20d9 100644 --- a/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift +++ b/ios/PacketTunnelCore/Actor/PacketTunnelActor.swift @@ -307,20 +307,11 @@ extension PacketTunnelActor { startDefaultPathObserver(notifyObserverWithCurrentPath: true) } - var daitaConfiguration: DaitaConfiguration? - if settings.daita.daitaState.isEnabled { - let maybeNot = Maybenot() - daitaConfiguration = DaitaConfiguration( - machines: maybeNot.machines, - maxEvents: maybeNot.maximumEvents, - maxActions: maybeNot.maximumActions - ) - } - + // Daita parameters are gotten from an ephemeral peer try await tunnelAdapter.startMultihop( entryConfiguration: configuration.entryConfiguration, exitConfiguration: configuration.exitConfiguration, - daita: daitaConfiguration + daita: nil ) // Resume tunnel monitoring and use IPv4 gateway as a probe address. diff --git a/ios/PacketTunnelCore/Actor/Protocols/EphemeralPeerExchangingProtocol.swift b/ios/PacketTunnelCore/Actor/Protocols/EphemeralPeerExchangingProtocol.swift index a0d596fd9e18..1155f516e25d 100644 --- a/ios/PacketTunnelCore/Actor/Protocols/EphemeralPeerExchangingProtocol.swift +++ b/ios/PacketTunnelCore/Actor/Protocols/EphemeralPeerExchangingProtocol.swift @@ -6,10 +6,15 @@ // Copyright © 2024 Mullvad VPN AB. All rights reserved. // +import MullvadTypes import WireGuardKitTypes public protocol EphemeralPeerExchangingProtocol { func start() async - func receivePostQuantumKey(_ preSharedKey: PreSharedKey, ephemeralKey: PrivateKey) async - func receiveEphemeralPeerPrivateKey(_: PrivateKey) async + func receivePostQuantumKey( + _ preSharedKey: PreSharedKey, + ephemeralKey: PrivateKey, + daitaParameters: DaitaV2Parameters? + ) async + func receiveEphemeralPeerPrivateKey(_: PrivateKey, daitaParameters: DaitaV2Parameters?) async } diff --git a/ios/PacketTunnelCore/Daita/Maybenot.swift b/ios/PacketTunnelCore/Daita/Maybenot.swift index fc5b0bd75cda..0bc682af753f 100644 --- a/ios/PacketTunnelCore/Daita/Maybenot.swift +++ b/ios/PacketTunnelCore/Daita/Maybenot.swift @@ -7,7 +7,9 @@ public struct Maybenot { 02eNqFjDkOgDAMBL056PkEJTVdvsY/+URKmojDmwiBUDyStbJ35DLuy5anWRrlqLTDcA0AkTXVkDt01ZAHwLFwludZeMsLLILlRRax4+lKcnrnl/8HJzqIIG0= 02eNpdjr0LQVEYxt9zGES5o4myKVksFM45lEHKx0LJYDAZ5C+QRTHIaGETViYDKYPp3ijFYLj3KkmGK2WwuVwiv3rrrafnA1Jm16ijKBQ+6NRDGPJ2E0Xe6YnAD0hFD9nm/ObIHumlv4imZ3Mm8DwvtaxB0Jy1IUex0pYIIB30lhuCYkYvwW6nQFSV41R1L8vkmwlgEOONVvEaYt1SwLhdhxn8gdBgEfXU7RFmzeR9YtPPxrbqOVkLsd29XJhIJfbqRomGheLcYUWeFgxQodrq9wcP1E88/w== """ - public let maximumEvents: UInt32 = 1000 - public let maximumActions: UInt32 = 1000 + public let maximumEvents: UInt32 = 2048 + public let maximumActions: UInt32 = 1024 + public let maximumPadding: Double = 1.0 + public let maximumBlocking: Double = 1.0 } // swiftlint:enable line_length diff --git a/mullvad-ios/src/ephemeral_peer_proxy/ios_tcp_connection.rs b/mullvad-ios/src/ephemeral_peer_proxy/ios_tcp_connection.rs index 3244e5f2d95c..d7f2260acd83 100644 --- a/mullvad-ios/src/ephemeral_peer_proxy/ios_tcp_connection.rs +++ b/mullvad-ios/src/ephemeral_peer_proxy/ios_tcp_connection.rs @@ -1,4 +1,3 @@ -use libc::c_void; use std::{ ffi::CStr, future::Future, @@ -71,19 +70,6 @@ impl WgTcpConnectionFunctions { } } -extern "C" { - /// Called when the preshared post quantum key is ready, - /// or when a Daita peer has been successfully requested. - /// `raw_preshared_key` will be NULL if: - /// - The post quantum key negotiation failed - /// - A Daita peer has been requested without enabling post quantum keys. - pub fn swift_ephemeral_peer_ready( - raw_packet_tunnel: *const c_void, - raw_preshared_key: *const u8, - raw_ephemeral_private_key: *const u8, - ); - -} #[derive(Clone)] pub struct IosTcpProvider { diff --git a/mullvad-ios/src/ephemeral_peer_proxy/mod.rs b/mullvad-ios/src/ephemeral_peer_proxy/mod.rs index 70d9347ad7a1..805bd2ef8c6f 100644 --- a/mullvad-ios/src/ephemeral_peer_proxy/mod.rs +++ b/mullvad-ios/src/ephemeral_peer_proxy/mod.rs @@ -2,11 +2,11 @@ pub mod ios_tcp_connection; pub mod peer_exchange; -use ios_tcp_connection::swift_ephemeral_peer_ready; use libc::c_void; use peer_exchange::EphemeralPeerExchange; -use std::{ptr, sync::Once}; +use std::{ffi::CString, ptr, sync::Once}; +use talpid_tunnel_config_client::DaitaSettings; static INIT_LOGGING: Once = Once::new(); #[derive(Clone)] @@ -17,17 +17,31 @@ pub struct PacketTunnelBridge { impl PacketTunnelBridge { fn fail_exchange(self) { - unsafe { swift_ephemeral_peer_ready(self.packet_tunnel, ptr::null(), ptr::null()) }; + unsafe { + swift_ephemeral_peer_ready(self.packet_tunnel, ptr::null(), ptr::null(), ptr::null()) + }; } - fn succeed_exchange(self, ephemeral_key: [u8; 32], preshared_key: Option<[u8; 32]>) { + fn succeed_exchange( + self, + ephemeral_key: [u8; 32], + preshared_key: Option<[u8; 32]>, + daita: Option, + ) { let ephemeral_ptr = ephemeral_key.as_ptr(); let preshared_ptr = preshared_key .as_ref() .map(|key| key.as_ptr()) .unwrap_or(ptr::null()); - unsafe { swift_ephemeral_peer_ready(self.packet_tunnel, preshared_ptr, ephemeral_ptr) }; + let daita_ptr = daita + .as_ref() + .map(|params| params as *const _) + .unwrap_or(ptr::null()); + + unsafe { + swift_ephemeral_peer_ready(self.packet_tunnel, preshared_ptr, ephemeral_ptr, daita_ptr) + }; } } @@ -42,6 +56,50 @@ pub struct EphemeralPeerParameters { pub funcs: ios_tcp_connection::WgTcpConnectionFunctions, } +#[repr(C)] +pub struct DaitaParameters { + pub machines: *mut u8, + pub max_padding_frac: f64, + pub max_blocking_frac: f64, +} + +impl DaitaParameters { + fn new(settings: DaitaSettings) -> Option { + let machines_string = settings.client_machines.join("\n"); + let machines = CString::new(machines_string).ok()?.into_raw().cast(); + Some(Self { + machines, + max_padding_frac: settings.max_padding_frac, + max_blocking_frac: settings.max_blocking_frac, + }) + } +} + +impl Drop for DaitaParameters { + fn drop(&mut self) { + // Safety: + // `machines` pointer must be a valid pointer to a CString. This can be achieved by + // ensuring that `DaitaParameters` are constructed via `DaitaParameters::new` and the + // `machines` pointer is never written to. + let _ = unsafe { CString::from_raw(self.machines.cast()) }; + } +} + +extern "C" { + /// Called when the preshared post quantum key is ready, + /// or when a Daita peer has been successfully requested. + /// `raw_preshared_key` will be NULL if: + /// - The post quantum key negotiation failed + /// - A Daita peer has been requested without enabling post quantum keys. + pub fn swift_ephemeral_peer_ready( + raw_packet_tunnel: *const c_void, + raw_preshared_key: *const u8, + raw_ephemeral_private_key: *const u8, + daita_parameters: *const DaitaParameters, + ); + +} + /// Called by the Swift side to signal that the ephemeral peer exchange should be cancelled. /// After this call, the cancel token is no longer valid. /// diff --git a/mullvad-ios/src/ephemeral_peer_proxy/peer_exchange.rs b/mullvad-ios/src/ephemeral_peer_proxy/peer_exchange.rs index ee9a512777e8..f65ccd016610 100644 --- a/mullvad-ios/src/ephemeral_peer_proxy/peer_exchange.rs +++ b/mullvad-ios/src/ephemeral_peer_proxy/peer_exchange.rs @@ -1,6 +1,8 @@ -use super::{ios_tcp_connection::*, EphemeralPeerParameters, PacketTunnelBridge}; +use super::{ios_tcp_connection::*, DaitaParameters, EphemeralPeerParameters, PacketTunnelBridge}; use std::{ffi::CStr, sync::Mutex, thread}; -use talpid_tunnel_config_client::{request_ephemeral_peer_with, Error, RelayConfigService}; +use talpid_tunnel_config_client::{ + request_ephemeral_peer_with, EphemeralPeer, Error, RelayConfigService, +}; use talpid_types::net::wireguard::{PrivateKey, PublicKey}; use tokio::{runtime::Handle as TokioHandle, task::JoinHandle}; use tonic::transport::channel::Endpoint; @@ -139,24 +141,15 @@ impl EphemeralPeerExchange { self.peer_parameters.enable_daita, ) => { match ephemeral_peer { - Ok(peer) => { - match peer.psk { - Some(preshared_key) => { - let preshared_key_bytes = *preshared_key.as_bytes(); - thread::spawn(move || { - let Self{ ephemeral_key, packet_tunnel, .. } = self; - packet_tunnel.succeed_exchange(ephemeral_key, Some(preshared_key_bytes)); - }); - - }, - None => { - // Daita peer was requested, but without enabling post quantum keys - thread::spawn(move || { - let Self{ ephemeral_key, packet_tunnel, .. } = self; - packet_tunnel.succeed_exchange(ephemeral_key, None); - }); - } - } + Ok(EphemeralPeer { psk, daita }) => { + thread::spawn(move || { + let Self{ ephemeral_key, packet_tunnel, .. } = self; + packet_tunnel.succeed_exchange( + ephemeral_key, + psk.map(|psk| *psk.as_bytes()), + daita.and_then(DaitaParameters::new) + ); + }); }, Err(error) => { log::error!("Key exchange failed {}", error); diff --git a/talpid-tunnel-config-client/src/lib.rs b/talpid-tunnel-config-client/src/lib.rs index 7c80d4f3e5fa..f2ce3f4993e4 100644 --- a/talpid-tunnel-config-client/src/lib.rs +++ b/talpid-tunnel-config-client/src/lib.rs @@ -22,7 +22,7 @@ mod proto { tonic::include_proto!("ephemeralpeer"); } -#[cfg(all(unix, not(target_os = "ios")))] +#[cfg(unix)] const DAITA_VERSION: u32 = 2; #[derive(Debug)] @@ -88,7 +88,7 @@ pub const CONFIG_SERVICE_PORT: u16 = 1337; pub struct EphemeralPeer { pub psk: Option, - #[cfg(all(unix, not(target_os = "ios")))] + #[cfg(unix)] pub daita: Option, } @@ -138,15 +138,15 @@ pub async fn request_ephemeral_peer_with( wg_parent_pubkey: parent_pubkey.as_bytes().to_vec(), wg_ephemeral_peer_pubkey: ephemeral_pubkey.as_bytes().to_vec(), post_quantum: pq_request, - #[cfg(any(windows, target_os = "ios"))] + #[cfg(windows)] daita: Some(proto::DaitaRequestV1 { activate_daita: enable_daita, }), - #[cfg(any(windows, target_os = "ios"))] + #[cfg(windows)] daita_v2: None, - #[cfg(all(unix, not(target_os = "ios")))] + #[cfg(unix)] daita: None, - #[cfg(all(unix, not(target_os = "ios")))] + #[cfg(unix)] daita_v2: enable_daita.then(|| proto::DaitaRequestV2 { level: i32::from(proto::DaitaLevel::LevelDefault), platform: i32::from(get_platform()), @@ -201,7 +201,7 @@ pub async fn request_ephemeral_peer_with( None }; - #[cfg(all(unix, not(target_os = "ios")))] + #[cfg(unix)] { let daita = response.daita.map(|daita| DaitaSettings { client_machines: daita.client_machines, @@ -214,13 +214,13 @@ pub async fn request_ephemeral_peer_with( Ok(EphemeralPeer { psk, daita }) } - #[cfg(any(windows, target_os = "ios"))] + #[cfg(windows)] { Ok(EphemeralPeer { psk }) } } -#[cfg(all(unix, not(target_os = "ios")))] +#[cfg(unix)] const fn get_platform() -> proto::DaitaPlatform { use proto::DaitaPlatform; const PLATFORM: DaitaPlatform = if cfg!(target_os = "windows") {