Skip to content

Commit fed889e

Browse files
committedDec 19, 2024
Merge branch 'ian-tcp'
2 parents 1e100f9 + ee5c10b commit fed889e

35 files changed

+897
-917
lines changed
 

‎Cargo.lock

+114-44
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎ios/MullvadRustRuntime/EphemeralPeerExchangeActor.swift

+30-61
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,9 @@ public protocol EphemeralPeerExchangeActorProtocol {
2121
public class EphemeralPeerExchangeActor: EphemeralPeerExchangeActorProtocol {
2222
struct Negotiation {
2323
var negotiator: EphemeralPeerNegotiating
24-
var inTunnelTCPConnection: NWTCPConnection
25-
var tcpConnectionObserver: NSKeyValueObservation
2624

2725
func cancel() {
2826
negotiator.cancelKeyNegotiation()
29-
tcpConnectionObserver.invalidate()
30-
inTunnelTCPConnection.cancel()
3127
}
3228
}
3329

@@ -54,15 +50,6 @@ public class EphemeralPeerExchangeActor: EphemeralPeerExchangeActorProtocol {
5450
self.keyExchangeRetriesIterator = iteratorProvider()
5551
}
5652

57-
private func createTCPConnection(_ gatewayEndpoint: NWHostEndpoint) -> NWTCPConnection {
58-
self.packetTunnel.createTCPConnectionThroughTunnel(
59-
to: gatewayEndpoint,
60-
enableTLS: false,
61-
tlsParameters: nil,
62-
delegate: nil
63-
)
64-
}
65-
6653
/// Starts a new key exchange.
6754
///
6855
/// Any ongoing key negotiation is stopped before starting a new one.
@@ -75,49 +62,46 @@ public class EphemeralPeerExchangeActor: EphemeralPeerExchangeActorProtocol {
7562
endCurrentNegotiation()
7663
let negotiator = negotiationProvider.init()
7764

78-
let gatewayAddress = LocalNetworkIPs.gatewayAddress.rawValue
79-
let IPv4Gateway = IPv4Address(gatewayAddress)!
80-
let endpoint = NWHostEndpoint(hostname: gatewayAddress, port: "\(CONFIG_SERVICE_PORT)")
81-
let inTunnelTCPConnection = createTCPConnection(endpoint)
82-
8365
// This will become the new private key of the device
8466
let ephemeralSharedKey = PrivateKey()
8567

8668
let tcpConnectionTimeout = keyExchangeRetriesIterator.next() ?? .seconds(10)
8769
// If the connection never becomes viable, force a reconnection after 10 seconds
88-
scheduleInTunnelConnectionTimeout(startTime: .now() + tcpConnectionTimeout)
89-
90-
let tcpConnectionObserver = inTunnelTCPConnection.observe(\.isViable, options: [
91-
.initial,
92-
.new,
93-
]) { [weak self] observedConnection, _ in
94-
guard let self, observedConnection.isViable else { return }
95-
self.negotiation?.tcpConnectionObserver.invalidate()
96-
self.timer?.cancel()
97-
98-
if !negotiator.startNegotiation(
99-
gatewayIP: IPv4Gateway,
100-
devicePublicKey: privateKey.publicKey,
101-
presharedKey: ephemeralSharedKey,
102-
peerReceiver: packetTunnel,
103-
tcpConnection: inTunnelTCPConnection,
104-
peerExchangeTimeout: tcpConnectionTimeout,
105-
enablePostQuantum: enablePostQuantum,
106-
enableDaita: enableDaita
107-
) {
108-
// Cancel the negotiation to shut down any remaining use of the TCP connection on the Rust side
109-
self.negotiation?.cancel()
110-
self.negotiation = nil
111-
self.onFailure()
112-
}
70+
let peerParameters = EphemeralPeerParameters(
71+
peer_exchange_timeout: UInt64(tcpConnectionTimeout.timeInterval),
72+
enable_post_quantum: enablePostQuantum,
73+
enable_daita: enableDaita,
74+
funcs: mapWgFunctions(functions: packetTunnel.wgFunctions())
75+
)
76+
77+
if !negotiator.startNegotiation(
78+
devicePublicKey: privateKey.publicKey,
79+
presharedKey: ephemeralSharedKey,
80+
peerReceiver: packetTunnel,
81+
ephemeralPeerParams: peerParameters
82+
) {
83+
// Cancel the negotiation to shut down any remaining use of the TCP connection on the Rust side
84+
self.negotiation?.cancel()
85+
self.negotiation = nil
86+
self.onFailure()
11387
}
88+
11489
negotiation = Negotiation(
115-
negotiator: negotiator,
116-
inTunnelTCPConnection: inTunnelTCPConnection,
117-
tcpConnectionObserver: tcpConnectionObserver
90+
negotiator: negotiator
11891
)
11992
}
12093

94+
private func mapWgFunctions(functions: WgFunctionPointers) -> WgTcpConnectionFunctions {
95+
var mappedFunctions = WgTcpConnectionFunctions()
96+
97+
mappedFunctions.close_fn = functions.close
98+
mappedFunctions.open_fn = functions.open
99+
mappedFunctions.send_fn = functions.send
100+
mappedFunctions.recv_fn = functions.receive
101+
102+
return mappedFunctions
103+
}
104+
121105
/// Cancels the ongoing key exchange.
122106
public func endCurrentNegotiation() {
123107
negotiation?.cancel()
@@ -129,19 +113,4 @@ public class EphemeralPeerExchangeActor: EphemeralPeerExchangeActorProtocol {
129113
keyExchangeRetriesIterator = iteratorProvider()
130114
endCurrentNegotiation()
131115
}
132-
133-
private func scheduleInTunnelConnectionTimeout(startTime: DispatchWallTime) {
134-
let newTimer = DispatchSource.makeTimerSource()
135-
136-
newTimer.setEventHandler { [weak self] in
137-
self?.onFailure()
138-
self?.timer?.cancel()
139-
}
140-
141-
newTimer.schedule(wallDeadline: startTime)
142-
newTimer.activate()
143-
144-
timer?.cancel()
145-
timer = newTimer
146-
}
147116
}

‎ios/MullvadRustRuntime/EphemeralPeerNegotiator.swift

+16-24
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,10 @@ import WireGuardKitTypes
1414
// swiftlint:disable function_parameter_count
1515
public protocol EphemeralPeerNegotiating {
1616
func startNegotiation(
17-
gatewayIP: IPv4Address,
1817
devicePublicKey: PublicKey,
1918
presharedKey: PrivateKey,
2019
peerReceiver: any TunnelProvider,
21-
tcpConnection: NWTCPConnection,
22-
peerExchangeTimeout: Duration,
23-
enablePostQuantum: Bool,
24-
enableDaita: Bool
20+
ephemeralPeerParams: EphemeralPeerParameters
2521
) -> Bool
2622

2723
func cancelKeyNegotiation()
@@ -33,49 +29,45 @@ public protocol EphemeralPeerNegotiating {
3329
public class EphemeralPeerNegotiator: EphemeralPeerNegotiating {
3430
required public init() {}
3531

36-
var cancelToken: EphemeralPeerCancelToken?
32+
var cancelToken: OpaquePointer?
3733

3834
public func startNegotiation(
39-
gatewayIP: IPv4Address,
4035
devicePublicKey: PublicKey,
4136
presharedKey: PrivateKey,
4237
peerReceiver: any TunnelProvider,
43-
tcpConnection: NWTCPConnection,
44-
peerExchangeTimeout: Duration,
45-
enablePostQuantum: Bool,
46-
enableDaita: Bool
38+
ephemeralPeerParams: EphemeralPeerParameters
4739
) -> Bool {
4840
// swiftlint:disable:next force_cast
4941
let ephemeralPeerReceiver = Unmanaged.passUnretained(peerReceiver as! EphemeralPeerReceiver)
5042
.toOpaque()
51-
let opaqueConnection = Unmanaged.passUnretained(tcpConnection).toOpaque()
52-
var cancelToken = EphemeralPeerCancelToken()
5343

54-
let result = request_ephemeral_peer(
44+
guard let tunnelHandle = try? peerReceiver.tunnelHandle() else {
45+
return false
46+
}
47+
48+
let cancelToken = request_ephemeral_peer(
5549
devicePublicKey.rawValue.map { $0 },
5650
presharedKey.rawValue.map { $0 },
5751
ephemeralPeerReceiver,
58-
opaqueConnection,
59-
&cancelToken,
60-
UInt64(peerExchangeTimeout.timeInterval),
61-
enablePostQuantum,
62-
enableDaita
52+
tunnelHandle,
53+
ephemeralPeerParams
6354
)
64-
guard result == 0 else {
55+
guard let cancelToken else {
6556
return false
6657
}
6758
self.cancelToken = cancelToken
6859
return true
6960
}
7061

7162
public func cancelKeyNegotiation() {
72-
guard var cancelToken else { return }
73-
cancel_ephemeral_peer_exchange(&cancelToken)
63+
guard let cancelToken else { return }
64+
cancel_ephemeral_peer_exchange(cancelToken)
65+
self.cancelToken = nil
7466
}
7567

7668
deinit {
77-
guard var cancelToken else { return }
78-
drop_ephemeral_peer_exchange_token(&cancelToken)
69+
guard let cancelToken else { return }
70+
drop_ephemeral_peer_exchange_token(cancelToken)
7971
}
8072
}
8173

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//
2+
// EphemeralPeerReceiver.swift
3+
// PacketTunnel
4+
//
5+
// Created by Marco Nikic on 2024-02-15.
6+
// Copyright © 2023 Mullvad VPN AB. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import MullvadRustRuntimeProxy
11+
import MullvadTypes
12+
import NetworkExtension
13+
import WireGuardKitTypes
14+
15+
/// End sequence of an ephemeral peer exchange.
16+
///
17+
/// This FFI function is called by Rust when an ephemeral peer negotiation succeeded or failed.
18+
/// When both the `rawPresharedKey` and the `rawEphemeralKey` are raw pointers to 32 bytes data arrays,
19+
/// the quantum-secure key exchange is considered successful.
20+
/// If the `rawPresharedKey` is nil, but there is a valid `rawEphemeralKey`, it means a Daita peer has been negotiated with.
21+
/// If `rawEphemeralKey` is nil, the negotiation is considered failed.
22+
///
23+
/// - Parameters:
24+
/// - rawEphemeralPeerReceiver: A raw pointer to the running instance of `NEPacketTunnelProvider`
25+
/// - rawPresharedKey: A raw pointer to the quantum-secure pre shared key
26+
/// - rawEphemeralKey: A raw pointer to the ephemeral private key of the device
27+
@_cdecl("swift_ephemeral_peer_ready")
28+
func receivePostQuantumKey(
29+
rawEphemeralPeerReceiver: UnsafeMutableRawPointer?,
30+
rawPresharedKey: UnsafeMutableRawPointer?,
31+
rawEphemeralKey: UnsafeMutableRawPointer?
32+
) {
33+
guard let rawEphemeralPeerReceiver else { return }
34+
let ephemeralPeerReceiver = Unmanaged<EphemeralPeerReceiver>.fromOpaque(rawEphemeralPeerReceiver)
35+
.takeUnretainedValue()
36+
37+
// If there are no private keys for the ephemeral peer, then the negotiation either failed, or timed out.
38+
guard let rawEphemeralKey,
39+
let ephemeralKey = PrivateKey(rawValue: Data(bytes: rawEphemeralKey, count: 32)) else {
40+
ephemeralPeerReceiver.ephemeralPeerExchangeFailed()
41+
return
42+
}
43+
44+
// If there is a pre-shared key, an ephemeral peer was negotiated with Post Quantum options
45+
// Otherwise, a Daita enabled ephemeral peer was requested
46+
if let rawPresharedKey, let key = PreSharedKey(rawValue: Data(bytes: rawPresharedKey, count: 32)) {
47+
ephemeralPeerReceiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey)
48+
} else {
49+
ephemeralPeerReceiver.receiveEphemeralPeerPrivateKey(ephemeralKey)
50+
}
51+
return
52+
}

‎ios/MullvadRustRuntime/PacketTunnelProvider+TCPConnection.swift

-118
This file was deleted.

0 commit comments

Comments
 (0)