Skip to content

Commit

Permalink
Use DAITAv2 on iOS
Browse files Browse the repository at this point in the history
  • Loading branch information
pinkisemils committed Dec 23, 2024
1 parent b1d8acb commit 1901d74
Show file tree
Hide file tree
Showing 25 changed files with 388 additions and 156 deletions.
5 changes: 5 additions & 0 deletions ios/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ Line wrap the file at 100 chars. Th
* **Fixed**: for any bug fixes.
* **Security**: in case of vulnerabilities.

## Unreleased
### Added
- Update to DAITA v2 - now machines are provided by relays dynamically instead
of using bundled ones.

## [2024.11 - 2024-12-12]
### Added
- Add WireGuard over Shadowsocks obfuscation. It can be enabled in "VPN settings". This will
Expand Down
27 changes: 23 additions & 4 deletions ios/MullvadRustRuntime/EphemeralPeerReceiver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<DaitaV2Parameters>?
) {
guard let rawEphemeralPeerReceiver else { return }
let ephemeralPeerReceiver = Unmanaged<EphemeralPeerReceiver>.fromOpaque(rawEphemeralPeerReceiver)
Expand All @@ -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
}
43 changes: 27 additions & 16 deletions ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -88,6 +94,22 @@ int32_t encrypted_dns_proxy_start(struct EncryptedDnsProxyState *encrypted_dns_p
*/
int32_t encrypted_dns_proxy_stop(struct ProxyHandle *proxy_config);

/**
* To be called when ephemeral peer exchange has finished. All parameters except
* `raw_packet_tunnel` are optional.
*
* # Safety:
* If the key exchange failed, all pointers except `raw_packet_tunnel` must be null. If the
* key exchange was successful, `raw_ephemeral_private_key` must be a valid pointer to 32
* bytes for the lifetime of this call. If PQ was enabled, `raw_preshared_key` must be a valid
* pointer to 32 bytes for the lifetime of this call. If DAITA was requested, the
* `daita_prameters` must point to a valid instance of `DaitaParameters`.
*/
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.
Expand All @@ -112,29 +134,18 @@ void drop_ephemeral_peer_exchange_token(struct ExchangeCancelToken *sender);
* Entry point for requesting ephemeral peers 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` must be valid pointers to a packet tunnel, the packet tunnel pointer must
* outlive the ephemeral peer exchange. `cancel_token` should be owned by the caller of this
* function.
* `public_key` and `ephemeral_key` must be valid respective `PublicKey` and `PrivateKey` types,
* specifically, they must be valid pointers to 32 bytes. They will not be valid after this
* function is called, and thus must be copied here. `packet_tunnel` must be valid pointers to a
* packet tunnel, the packet tunnel pointer must outlive the ephemeral peer exchange.
* `cancel_token` should be owned by the caller of this function.
*/
struct ExchangeCancelToken *request_ephemeral_peer(const uint8_t *public_key,
const uint8_t *ephemeral_key,
const void *packet_tunnel,
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
Expand Down
31 changes: 31 additions & 0 deletions ios/MullvadTypes/Protocols/DaitaV2Parameters.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
15 changes: 11 additions & 4 deletions ios/MullvadTypes/Protocols/EphemeralPeerReceiver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
9 changes: 6 additions & 3 deletions ios/MullvadTypes/Protocols/EphemeralPeerReceiving.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"originHash" : "c15149b2d59d9e9c72375f65339c04f41a19943e1117e682df27fc9f943fdc56",
"pins" : [
{
"identity" : "swift-log",
Expand All @@ -19,5 +18,5 @@
}
}
],
"version" : 3
"version" : 2
}
22 changes: 18 additions & 4 deletions ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -376,12 +376,26 @@ 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() {
Expand Down
23 changes: 19 additions & 4 deletions ios/PacketTunnel/PostQuantum/EphemeralPeerExchangingPipeline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import MullvadRustRuntime
import MullvadSettings
import MullvadTypes
import PacketTunnelCore
import WireGuardKitTypes

Expand Down Expand Up @@ -59,11 +60,25 @@ 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
)
}
}
26 changes: 19 additions & 7 deletions ios/PacketTunnel/PostQuantum/MultiHopEphemeralPeerExchanger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down Expand Up @@ -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()
Expand All @@ -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()
Expand All @@ -95,7 +102,8 @@ final class MultiHopEphemeralPeerExchanger: EphemeralPeerExchangingProtocol {
relay: entry,
configuration: EphemeralPeerConfiguration(
privateKey: devicePrivateKey,
allowedIPs: defaultGatewayAddressRange
allowedIPs: defaultGatewayAddressRange,
daitaParameters: daitaParameters
)
)))
keyExchanger.startNegotiation(
Expand All @@ -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
)
)
))
Expand All @@ -140,15 +150,17 @@ 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: exitPeerKey.ephemeralKey,
preSharedKey: exitPeerKey.preSharedKey,
allowedIPs: allTrafficRange
allowedIPs: allTrafficRange,
daitaParameters: self.daitaParameters
)
)
))
Expand Down
Loading

0 comments on commit 1901d74

Please sign in to comment.