Skip to content

Commit 25381c8

Browse files
committed
Use DAITAv2 on iOS
1 parent f9e09b7 commit 25381c8

24 files changed

+389
-154
lines changed

ios/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ Line wrap the file at 100 chars. Th
2121
* **Fixed**: for any bug fixes.
2222
* **Security**: in case of vulnerabilities.
2323

24+
## Unreleased
25+
### Added
26+
- Update to DAITA v2 - now machines are provided by relays dynamically instead
27+
of using bundled ones.
28+
2429
## [2024.11 - 2024-12-12]
2530
### Added
2631
- Add WireGuard over Shadowsocks obfuscation. It can be enabled in "VPN settings". This will

ios/MullvadRustRuntime/EphemeralPeerReceiver.swift

+23-4
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ import WireGuardKitTypes
2424
/// - rawEphemeralPeerReceiver: A raw pointer to the running instance of `NEPacketTunnelProvider`
2525
/// - rawPresharedKey: A raw pointer to the quantum-secure pre shared key
2626
/// - rawEphemeralKey: A raw pointer to the ephemeral private key of the device
27-
@_cdecl("swift_ephemeral_peer_ready")
27+
/// - rawDaitaParameters: A raw pointer to negotiated DAITA parameters
28+
@_silgen_name("swift_ephemeral_peer_ready")
2829
func receivePostQuantumKey(
2930
rawEphemeralPeerReceiver: UnsafeMutableRawPointer?,
3031
rawPresharedKey: UnsafeMutableRawPointer?,
31-
rawEphemeralKey: UnsafeMutableRawPointer?
32+
rawEphemeralKey: UnsafeMutableRawPointer?,
33+
rawDaitaParameters: UnsafePointer<DaitaV2Parameters>?
3234
) {
3335
guard let rawEphemeralPeerReceiver else { return }
3436
let ephemeralPeerReceiver = Unmanaged<EphemeralPeerReceiver>.fromOpaque(rawEphemeralPeerReceiver)
@@ -41,12 +43,29 @@ func receivePostQuantumKey(
4143
return
4244
}
4345

46+
let maybeNot = Maybenot()
47+
let daitaParameters: DaitaV2Parameters? = rawDaitaParameters?.withMemoryRebound(
48+
to: DaitaParameters.self,
49+
capacity: 1
50+
) { body in
51+
let params = body.pointee
52+
guard params.machines != nil else { return nil }
53+
let machines = String(cString: params.machines)
54+
return DaitaV2Parameters(
55+
machines: machines,
56+
maximumEvents: maybeNot.maximumEvents,
57+
maximumActions: maybeNot.maximumActions,
58+
maximumPadding: params.max_padding_frac,
59+
maximumBlocking: params.max_blocking_frac
60+
)
61+
}
62+
4463
// If there is a pre-shared key, an ephemeral peer was negotiated with Post Quantum options
4564
// Otherwise, a Daita enabled ephemeral peer was requested
4665
if let rawPresharedKey, let key = PreSharedKey(rawValue: Data(bytes: rawPresharedKey, count: 32)) {
47-
ephemeralPeerReceiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey)
66+
ephemeralPeerReceiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey, daitaParameters: daitaParameters)
4867
} else {
49-
ephemeralPeerReceiver.receiveEphemeralPeerPrivateKey(ephemeralKey)
68+
ephemeralPeerReceiver.receiveEphemeralPeerPrivateKey(ephemeralKey, daitaParameters: daitaParameters)
5069
}
5170
return
5271
}

ios/MullvadRustRuntime/include/mullvad_rust_runtime.h

+27-16
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ typedef struct ProxyHandle {
2727
uint16_t port;
2828
} ProxyHandle;
2929

30+
typedef struct DaitaParameters {
31+
uint8_t *machines;
32+
double max_padding_frac;
33+
double max_blocking_frac;
34+
} DaitaParameters;
35+
3036
typedef struct WgTcpConnectionFunctions {
3137
int32_t (*open_fn)(int32_t tunnelHandle, const char *address, uint64_t timeout);
3238
int32_t (*close_fn)(int32_t tunnelHandle, int32_t socketHandle);
@@ -88,6 +94,22 @@ int32_t encrypted_dns_proxy_start(struct EncryptedDnsProxyState *encrypted_dns_p
8894
*/
8995
int32_t encrypted_dns_proxy_stop(struct ProxyHandle *proxy_config);
9096

97+
/**
98+
* To be called when ephemeral peer exchange has finished. All parameters except
99+
* `raw_packet_tunnel` are optional.
100+
*
101+
* # Safety:
102+
* If the key exchange failed, all pointers except `raw_packet_tunnel` must be null. If the
103+
* key exchange was successful, `raw_ephemeral_private_key` must be a valid pointer to 32
104+
* bytes for the lifetime of this call. If PQ was enabled, `raw_preshared_key` must be a valid
105+
* pointer to 32 bytes for the lifetime of this call. If DAITA was requested, the
106+
* `daita_prameters` must point to a valid instance of `DaitaParameters`.
107+
*/
108+
extern void swift_ephemeral_peer_ready(const void *raw_packet_tunnel,
109+
const uint8_t *raw_preshared_key,
110+
const uint8_t *raw_ephemeral_private_key,
111+
const struct DaitaParameters *daita_parameters);
112+
91113
/**
92114
* Called by the Swift side to signal that the ephemeral peer exchange should be cancelled.
93115
* After this call, the cancel token is no longer valid.
@@ -112,29 +134,18 @@ void drop_ephemeral_peer_exchange_token(struct ExchangeCancelToken *sender);
112134
* Entry point for requesting ephemeral peers on iOS.
113135
* The TCP connection must be created to go through the tunnel.
114136
* # Safety
115-
* `public_key` and `ephemeral_key` must be valid respective `PublicKey` and `PrivateKey` types.
116-
* They will not be valid after this function is called, and thus must be copied here.
117-
* `packet_tunnel` must be valid pointers to a packet tunnel, the packet tunnel pointer must
118-
* outlive the ephemeral peer exchange. `cancel_token` should be owned by the caller of this
119-
* function.
137+
* `public_key` and `ephemeral_key` must be valid respective `PublicKey` and `PrivateKey` types,
138+
* specifically, they must be valid pointers to 32 bytes. They will not be valid after this
139+
* function is called, and thus must be copied here. `packet_tunnel` must be valid pointers to a
140+
* packet tunnel, the packet tunnel pointer must outlive the ephemeral peer exchange.
141+
* `cancel_token` should be owned by the caller of this function.
120142
*/
121143
struct ExchangeCancelToken *request_ephemeral_peer(const uint8_t *public_key,
122144
const uint8_t *ephemeral_key,
123145
const void *packet_tunnel,
124146
int32_t tunnel_handle,
125147
struct EphemeralPeerParameters peer_parameters);
126148

127-
/**
128-
* Called when the preshared post quantum key is ready,
129-
* or when a Daita peer has been successfully requested.
130-
* `raw_preshared_key` will be NULL if:
131-
* - The post quantum key negotiation failed
132-
* - A Daita peer has been requested without enabling post quantum keys.
133-
*/
134-
extern void swift_ephemeral_peer_ready(const void *raw_packet_tunnel,
135-
const uint8_t *raw_preshared_key,
136-
const uint8_t *raw_ephemeral_private_key);
137-
138149
/**
139150
* # Safety
140151
* `addr`, `password`, `cipher` must be valid for the lifetime of this function call and they must
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// DaitaV2Parameters.swift
3+
// MullvadTypes
4+
//
5+
// Created by Marco Nikic on 2024-11-12.
6+
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
public struct DaitaV2Parameters: Equatable {
12+
public let machines: String
13+
public let maximumEvents: UInt32
14+
public let maximumActions: UInt32
15+
public let maximumPadding: Double
16+
public let maximumBlocking: Double
17+
18+
public init(
19+
machines: String,
20+
maximumEvents: UInt32,
21+
maximumActions: UInt32,
22+
maximumPadding: Double,
23+
maximumBlocking: Double
24+
) {
25+
self.machines = machines
26+
self.maximumEvents = maximumEvents
27+
self.maximumActions = maximumActions
28+
self.maximumPadding = maximumPadding
29+
self.maximumBlocking = maximumBlocking
30+
}
31+
}

ios/MullvadTypes/Protocols/EphemeralPeerReceiver.swift

+11-4
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,26 @@ public class EphemeralPeerReceiver: EphemeralPeerReceiving, TunnelProvider {
2929

3030
// MARK: - EphemeralPeerReceiving
3131

32-
public func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) {
32+
public func receivePostQuantumKey(
33+
_ key: PreSharedKey,
34+
ephemeralKey: PrivateKey,
35+
daitaParameters: DaitaV2Parameters?
36+
) {
3337
let semaphore = DispatchSemaphore(value: 0)
3438
Task {
35-
await keyReceiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey)
39+
await keyReceiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey, daitaParameters: daitaParameters)
3640
semaphore.signal()
3741
}
3842
semaphore.wait()
3943
}
4044

41-
public func receiveEphemeralPeerPrivateKey(_ ephemeralPeerPrivateKey: PrivateKey) {
45+
public func receiveEphemeralPeerPrivateKey(
46+
_ ephemeralPeerPrivateKey: PrivateKey,
47+
daitaParameters: DaitaV2Parameters?
48+
) {
4249
let semaphore = DispatchSemaphore(value: 0)
4350
Task {
44-
await keyReceiver.receiveEphemeralPeerPrivateKey(ephemeralPeerPrivateKey)
51+
await keyReceiver.receiveEphemeralPeerPrivateKey(ephemeralPeerPrivateKey, daitaParameters: daitaParameters)
4552
semaphore.signal()
4653
}
4754
semaphore.wait()

ios/MullvadTypes/Protocols/EphemeralPeerReceiving.swift

+6-3
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@ public protocol EphemeralPeerReceiving {
1515
/// - Parameters:
1616
/// - key: The preshared key used by the Ephemeral Peer
1717
/// - ephemeralKey: The private key used by the Ephemeral Peer
18-
func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) async
18+
/// - daitaParameters: DAITA parameters
19+
func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey, daitaParameters: DaitaV2Parameters?) async
1920

2021
/// Called when successfully requesting an ephemeral peer with Daita enabled, and Post Quantum PSK disabled
21-
/// - Parameter _:_ The private key used by the Ephemeral Peer
22-
func receiveEphemeralPeerPrivateKey(_: PrivateKey) async
22+
/// - Parameters:
23+
/// - _: The private key used by the Ephemeral Peer
24+
/// - daitaParameters: DAITA parameters
25+
func receiveEphemeralPeerPrivateKey(_: PrivateKey, daitaParameters: DaitaV2Parameters?) async
2326

2427
/// Called when an ephemeral peer could not be successfully negotiated
2528
func ephemeralPeerExchangeFailed()

ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift

+18-4
Original file line numberDiff line numberDiff line change
@@ -376,12 +376,26 @@ extension PacketTunnelProvider {
376376
}
377377

378378
extension PacketTunnelProvider: EphemeralPeerReceiving {
379-
func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) async {
380-
await ephemeralPeerExchangingPipeline.receivePostQuantumKey(key, ephemeralKey: ephemeralKey)
379+
func receivePostQuantumKey(
380+
_ key: PreSharedKey,
381+
ephemeralKey: PrivateKey,
382+
daitaParameters: MullvadTypes.DaitaV2Parameters?
383+
) async {
384+
await ephemeralPeerExchangingPipeline.receivePostQuantumKey(
385+
key,
386+
ephemeralKey: ephemeralKey,
387+
daitaParameters: daitaParameters
388+
)
381389
}
382390

383-
public func receiveEphemeralPeerPrivateKey(_ ephemeralPeerPrivateKey: PrivateKey) async {
384-
await ephemeralPeerExchangingPipeline.receiveEphemeralPeerPrivateKey(ephemeralPeerPrivateKey)
391+
public func receiveEphemeralPeerPrivateKey(
392+
_ ephemeralPeerPrivateKey: PrivateKey,
393+
daitaParameters: MullvadTypes.DaitaV2Parameters?
394+
) async {
395+
await ephemeralPeerExchangingPipeline.receiveEphemeralPeerPrivateKey(
396+
ephemeralPeerPrivateKey,
397+
daitaParameters: daitaParameters
398+
)
385399
}
386400

387401
func ephemeralPeerExchangeFailed() {

ios/PacketTunnel/PostQuantum/EphemeralPeerExchangingPipeline.swift

+19-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import MullvadRustRuntime
1010
import MullvadSettings
11+
import MullvadTypes
1112
import PacketTunnelCore
1213
import WireGuardKitTypes
1314

@@ -59,11 +60,25 @@ final public class EphemeralPeerExchangingPipeline {
5960
await ephemeralPeerExchanger.start()
6061
}
6162

62-
public func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) async {
63-
await ephemeralPeerExchanger.receivePostQuantumKey(key, ephemeralKey: ephemeralKey)
63+
public func receivePostQuantumKey(
64+
_ key: PreSharedKey,
65+
ephemeralKey: PrivateKey,
66+
daitaParameters: DaitaV2Parameters?
67+
) async {
68+
await ephemeralPeerExchanger.receivePostQuantumKey(
69+
key,
70+
ephemeralKey: ephemeralKey,
71+
daitaParameters: daitaParameters
72+
)
6473
}
6574

66-
public func receiveEphemeralPeerPrivateKey(_ ephemeralPeerPrivateKey: PrivateKey) async {
67-
await ephemeralPeerExchanger.receiveEphemeralPeerPrivateKey(ephemeralPeerPrivateKey)
75+
public func receiveEphemeralPeerPrivateKey(
76+
_ ephemeralPeerPrivateKey: PrivateKey,
77+
daitaParameters: DaitaV2Parameters?
78+
) async {
79+
await ephemeralPeerExchanger.receiveEphemeralPeerPrivateKey(
80+
ephemeralPeerPrivateKey,
81+
daitaParameters: daitaParameters
82+
)
6883
}
6984
}

ios/PacketTunnel/PostQuantum/MultiHopEphemeralPeerExchanger.swift

+19-7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol {
2525

2626
private var entryPeerKey: EphemeralPeerKey!
2727
private var exitPeerKey: EphemeralPeerKey!
28+
private var daitaParameters: DaitaV2Parameters?
2829

2930
private let defaultGatewayAddressRange = [IPAddressRange(from: "\(LocalNetworkIPs.gatewayAddress.rawValue)/32")!]
3031
private let allTrafficRange = [
@@ -66,7 +67,11 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol {
6667
await negotiateWithEntry()
6768
}
6869

69-
public func receiveEphemeralPeerPrivateKey(_ ephemeralPeerPrivateKey: PrivateKey) async {
70+
public func receiveEphemeralPeerPrivateKey(
71+
_ ephemeralPeerPrivateKey: PrivateKey,
72+
daitaParameters: DaitaV2Parameters?
73+
) async {
74+
self.daitaParameters = daitaParameters
7075
if state == .negotiatingWithEntry {
7176
entryPeerKey = EphemeralPeerKey(ephemeralKey: ephemeralPeerPrivateKey)
7277
await negotiateBetweenEntryAndExit()
@@ -78,8 +83,10 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol {
7883

7984
func receivePostQuantumKey(
8085
_ preSharedKey: PreSharedKey,
81-
ephemeralKey: PrivateKey
86+
ephemeralKey: PrivateKey,
87+
daitaParameters: DaitaV2Parameters?
8288
) async {
89+
self.daitaParameters = daitaParameters
8390
if state == .negotiatingWithEntry {
8491
entryPeerKey = EphemeralPeerKey(preSharedKey: preSharedKey, ephemeralKey: ephemeralKey)
8592
await negotiateBetweenEntryAndExit()
@@ -95,7 +102,8 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol {
95102
relay: entry,
96103
configuration: EphemeralPeerConfiguration(
97104
privateKey: devicePrivateKey,
98-
allowedIPs: defaultGatewayAddressRange
105+
allowedIPs: defaultGatewayAddressRange,
106+
daitaParameters: daitaParameters
99107
)
100108
)))
101109
keyExchanger.startNegotiation(
@@ -113,14 +121,16 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol {
113121
configuration: EphemeralPeerConfiguration(
114122
privateKey: entryPeerKey.ephemeralKey,
115123
preSharedKey: entryPeerKey.preSharedKey,
116-
allowedIPs: [IPAddressRange(from: "\(exit.endpoint.ipv4Relay.ip)/32")!]
124+
allowedIPs: [IPAddressRange(from: "\(exit.endpoint.ipv4Relay.ip)/32")!],
125+
daitaParameters: self.daitaParameters
117126
)
118127
),
119128
exit: EphemeralPeerRelayConfiguration(
120129
relay: exit,
121130
configuration: EphemeralPeerConfiguration(
122131
privateKey: devicePrivateKey,
123-
allowedIPs: defaultGatewayAddressRange
132+
allowedIPs: defaultGatewayAddressRange,
133+
daitaParameters: self.daitaParameters
124134
)
125135
)
126136
))
@@ -140,15 +150,17 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol {
140150
configuration: EphemeralPeerConfiguration(
141151
privateKey: entryPeerKey.ephemeralKey,
142152
preSharedKey: entryPeerKey.preSharedKey,
143-
allowedIPs: [IPAddressRange(from: "\(exit.endpoint.ipv4Relay.ip)/32")!]
153+
allowedIPs: [IPAddressRange(from: "\(exit.endpoint.ipv4Relay.ip)/32")!],
154+
daitaParameters: self.daitaParameters
144155
)
145156
),
146157
exit: EphemeralPeerRelayConfiguration(
147158
relay: exit,
148159
configuration: EphemeralPeerConfiguration(
149160
privateKey: exitPeerKey.ephemeralKey,
150161
preSharedKey: exitPeerKey.preSharedKey,
151-
allowedIPs: allTrafficRange
162+
allowedIPs: allTrafficRange,
163+
daitaParameters: self.daitaParameters
152164
)
153165
)
154166
))

0 commit comments

Comments
 (0)