diff options
| author | David Lönnhager <david.l@mullvad.net> | 2023-02-21 11:07:22 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2023-02-21 11:07:22 +0100 |
| commit | 00aeed11ab5870158622108c7ac17a3a10a60d3a (patch) | |
| tree | b8c33f1957400bb4cc6429cfd91165018ada6237 | |
| parent | a83211930c6473776d2e838e357cc5583000aa9f (diff) | |
| parent | ec8a7e1e729d9e25f363c3a7394acd39ddb36f42 (diff) | |
| download | mullvadvpn-00aeed11ab5870158622108c7ac17a3a10a60d3a.tar.xz mullvadvpn-00aeed11ab5870158622108c7ac17a3a10a60d3a.zip | |
Merge branch 'make-pq-optional'
23 files changed, 521 insertions, 123 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 971a71edda..86d19580ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,9 @@ Line wrap the file at 100 chars. Th ### Changed - Update the Post-Quantum secure key exchange gRPC client to use the stabilized `PskExchangeV1` endpoint +- Add "auto" setting for the quantum-resistant tunnel feature, and make it the default. If it was + previously set to off, it will now be set to auto instead. That currently means the same thing as + "off", but this might change in the future. #### Windows - Remove automatic fallback to wireguard-go. This is done as a first step before fully diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardTunnelOptions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardTunnelOptions.kt index 251571021a..85a5ebc894 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardTunnelOptions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardTunnelOptions.kt @@ -2,7 +2,9 @@ package net.mullvad.mullvadvpn.model import android.os.Parcelable import kotlinx.parcelize.Parcelize -import net.mullvad.talpid.net.wireguard.TunnelOptions as TalpidWireguardTunnelOptions @Parcelize -data class WireguardTunnelOptions(val options: TalpidWireguardTunnelOptions) : Parcelable +data class WireguardTunnelOptions( + val mtu: Int?, + val quantumResistant: Boolean? +) : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/AdvancedFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/AdvancedFragment.kt index 011e9fdc15..10a4f2b5d7 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/AdvancedFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/AdvancedFragment.kt @@ -229,7 +229,7 @@ class AdvancedFragment : BaseFragment() { private fun updateUi(settings: Settings) { if (this::wireguardMtuInput.isInitialized && wireguardMtuInput.hasFocus == false) { - wireguardMtuInput.value = settings.tunnelOptions.wireguard.options.mtu + wireguardMtuInput.value = settings.tunnelOptions.wireguard.mtu } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/SettingsListener.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/SettingsListener.kt index a1464a7745..1cb1cf2986 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/SettingsListener.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/SettingsListener.kt @@ -29,7 +29,7 @@ class SettingsListener(private val connection: Messenger, eventDispatcher: Event } var wireguardMtu: Int? - get() = settingsNotifier.latestEvent?.tunnelOptions?.wireguard?.options?.mtu + get() = settingsNotifier.latestEvent?.tunnelOptions?.wireguard?.mtu set(value) { connection.send(Request.SetWireGuardMtu(value).message) } diff --git a/android/app/src/main/kotlin/net/mullvad/talpid/net/wireguard/TunnelOptions.kt b/android/app/src/main/kotlin/net/mullvad/talpid/net/wireguard/TunnelOptions.kt deleted file mode 100644 index 79e8ce544c..0000000000 --- a/android/app/src/main/kotlin/net/mullvad/talpid/net/wireguard/TunnelOptions.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.mullvad.talpid.net.wireguard - -import android.os.Parcelable -import kotlinx.parcelize.Parcelize - -@Parcelize -data class TunnelOptions( - val mtu: Int?, - val usePqSafePsk: Boolean -) : Parcelable diff --git a/mullvad-cli/src/cmds/relay.rs b/mullvad-cli/src/cmds/relay.rs index 981f0fffdd..27e8df55b9 100644 --- a/mullvad-cli/src/cmds/relay.rs +++ b/mullvad-cli/src/cmds/relay.rs @@ -593,7 +593,7 @@ impl Relay { wireguard_constraints.entry_location = parse_entry_location_constraint(entry); let use_multihop = wireguard_constraints.entry_location.is_some(); if use_multihop { - let use_pq_safe_psk = rpc + let quantum_resistant = rpc .get_settings(()) .await? .into_inner() @@ -601,8 +601,12 @@ impl Relay { .unwrap() .wireguard .unwrap() - .use_pq_safe_psk; - if use_pq_safe_psk { + .quantum_resistant; + if quantum_resistant + == Some(types::QuantumResistantState { + state: i32::from(types::quantum_resistant_state::State::On), + }) + { return Err(Error::CommandFailed( "Quantum resistant tunnels do not work when multihop is enabled", )); diff --git a/mullvad-cli/src/cmds/tunnel.rs b/mullvad-cli/src/cmds/tunnel.rs index a40aa985b9..042590fcbb 100644 --- a/mullvad-cli/src/cmds/tunnel.rs +++ b/mullvad-cli/src/cmds/tunnel.rs @@ -63,7 +63,13 @@ fn create_wireguard_quantum_resistant_tunnel_subcommand() -> clap::App<'static> .about("Controls the quantum-resistant PSK exchange in the tunnel") .setting(clap::AppSettings::SubcommandRequiredElseHelp) .subcommand(clap::App::new("get")) - .subcommand(clap::App::new("set").arg(clap::Arg::new("policy").required(true))) + .subcommand( + clap::App::new("set").arg( + clap::Arg::new("policy") + .required(true) + .possible_values(["on", "off", "auto"]), + ), + ) } fn create_wireguard_keys_subcommand() -> clap::App<'static> { @@ -222,10 +228,15 @@ impl Tunnel { async fn process_wireguard_quantum_resistant_tunnel_get() -> Result<()> { let tunnel_options = Self::get_tunnel_options().await?; - if tunnel_options.wireguard.unwrap().use_pq_safe_psk { - println!("enabled"); - } else { - println!("disabled"); + match tunnel_options + .wireguard + .unwrap() + .quantum_resistant + .and_then(|state| types::quantum_resistant_state::State::from_i32(state.state)) + { + Some(types::quantum_resistant_state::State::On) => println!("enabled"), + Some(types::quantum_resistant_state::State::Off) => println!("disabled"), + None | Some(types::quantum_resistant_state::State::Auto) => println!("auto"), } Ok(()) } @@ -233,10 +244,15 @@ impl Tunnel { async fn process_wireguard_quantum_resistant_tunnel_set( matches: &clap::ArgMatches, ) -> Result<()> { - let use_pq_safe_psk = matches.value_of("policy").unwrap() == "on"; + let quantum_resistant = match matches.value_of("policy").unwrap() { + "auto" => types::quantum_resistant_state::State::Auto, + "on" => types::quantum_resistant_state::State::On, + "off" => types::quantum_resistant_state::State::Off, + _ => unreachable!("invalid PQ state"), + }; let mut rpc = new_rpc_client().await?; let settings = rpc.get_settings(()).await?; - if use_pq_safe_psk { + if quantum_resistant == types::quantum_resistant_state::State::On { let multihop_is_enabled = settings .into_inner() .relay_settings @@ -256,7 +272,10 @@ impl Tunnel { )); } } - rpc.set_quantum_resistant_tunnel(use_pq_safe_psk).await?; + rpc.set_quantum_resistant_tunnel(types::QuantumResistantState { + state: i32::from(quantum_resistant), + }) + .await?; println!("Updated quantum resistant tunnel setting"); Ok(()) } diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 4bb22a9aa8..6a71e9d754 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -49,7 +49,7 @@ use mullvad_types::{ settings::{DnsOptions, Settings}, states::{TargetState, TunnelState}, version::{AppVersion, AppVersionInfo}, - wireguard::{PublicKey, RotationInterval}, + wireguard::{PublicKey, QuantumResistantState, RotationInterval}, }; use settings::SettingsPersister; #[cfg(target_os = "android")] @@ -226,7 +226,7 @@ pub enum DaemonCommand { /// Set if IPv6 should be enabled in the tunnel SetEnableIpv6(ResponseTx<(), settings::Error>, bool), /// Set whether to enable PQ PSK exchange in the tunnel - SetQuantumResistantTunnel(ResponseTx<(), settings::Error>, bool), + SetQuantumResistantTunnel(ResponseTx<(), settings::Error>, QuantumResistantState), /// Set DNS options or servers to use SetDnsOptions(ResponseTx<(), settings::Error>, DnsOptions), /// Toggle macOS network check leak @@ -1004,8 +1004,9 @@ where } SetBridgeState(tx, bridge_state) => self.on_set_bridge_state(tx, bridge_state).await, SetEnableIpv6(tx, enable_ipv6) => self.on_set_enable_ipv6(tx, enable_ipv6).await, - SetQuantumResistantTunnel(tx, enable_pq) => { - self.on_set_quantum_resistant_tunnel(tx, enable_pq).await + SetQuantumResistantTunnel(tx, quantum_resistant_state) => { + self.on_set_quantum_resistant_tunnel(tx, quantum_resistant_state) + .await } SetDnsOptions(tx, dns_servers) => self.on_set_dns_options(tx, dns_servers).await, SetWireguardMtu(tx, mtu) => self.on_set_wireguard_mtu(tx, mtu).await, @@ -2019,11 +2020,11 @@ where async fn on_set_quantum_resistant_tunnel( &mut self, tx: ResponseTx<(), settings::Error>, - use_pq_safe_psk: bool, + quantum_resistant: QuantumResistantState, ) { let save_result = self .settings - .set_quantum_resistant_tunnel(use_pq_safe_psk) + .set_quantum_resistant_tunnel(quantum_resistant) .await; match save_result { Ok(settings_changed) => { diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index a66ee7a7db..c231c8a49e 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -345,11 +345,16 @@ impl ManagementService for ManagementServiceImpl { .map_err(map_settings_error) } - async fn set_quantum_resistant_tunnel(&self, request: Request<bool>) -> ServiceResult<()> { - let enable = request.into_inner(); - log::debug!("set_quantum_resistant_tunnel({})", enable); + async fn set_quantum_resistant_tunnel( + &self, + request: Request<types::QuantumResistantState>, + ) -> ServiceResult<()> { + let state = mullvad_types::wireguard::QuantumResistantState::try_from(request.into_inner()) + .map_err(map_protobuf_type_err)?; + + log::debug!("set_quantum_resistant_tunnel({state:?})"); let (tx, rx) = oneshot::channel(); - self.send_command_to_daemon(DaemonCommand::SetQuantumResistantTunnel(tx, enable))?; + self.send_command_to_daemon(DaemonCommand::SetQuantumResistantTunnel(tx, state))?; self.wait_for_result(rx) .await? .map(Response::new) diff --git a/mullvad-daemon/src/migrations/mod.rs b/mullvad-daemon/src/migrations/mod.rs index c6bfc5babe..f2edfc1147 100644 --- a/mullvad-daemon/src/migrations/mod.rs +++ b/mullvad-daemon/src/migrations/mod.rs @@ -50,6 +50,7 @@ mod v2; mod v3; mod v4; mod v5; +mod v6; const SETTINGS_FILE: &str = "settings.json"; @@ -95,7 +96,7 @@ pub type Result<T> = std::result::Result<T, Error>; /// Returns whether there is any background work remaining. #[derive(Clone)] -pub(crate) struct MigrationComplete(Arc<AtomicBool>); +pub struct MigrationComplete(Arc<AtomicBool>); impl MigrationComplete { pub fn new(state: bool) -> Self { @@ -112,12 +113,9 @@ impl MigrationComplete { } /// Contains discarded data that may be useful for later work. -pub(crate) type MigrationData = v5::MigrationData; +pub type MigrationData = v5::MigrationData; -pub(crate) async fn migrate_all( - cache_dir: &Path, - settings_dir: &Path, -) -> Result<Option<MigrationData>> { +pub async fn migrate_all(cache_dir: &Path, settings_dir: &Path) -> Result<Option<MigrationData>> { #[cfg(windows)] windows::migrate_after_windows_update(settings_dir) .await @@ -149,6 +147,7 @@ pub(crate) async fn migrate_all( account_history::migrate_formats(settings_dir, &mut settings).await?; let migration_data = v5::migrate(&mut settings)?; + v6::migrate(&mut settings)?; if settings == old_settings { // Nothing changed diff --git a/mullvad-daemon/src/migrations/v5.rs b/mullvad-daemon/src/migrations/v5.rs index 2c2942cd4e..cfce5c696d 100644 --- a/mullvad-daemon/src/migrations/v5.rs +++ b/mullvad-daemon/src/migrations/v5.rs @@ -57,7 +57,7 @@ pub struct MigrationData { /// /// The ability to disable WireGuard multihop while preserving the entry location was added. /// So a new field, `use_multihop` is introduced. We want this to default to `true` iff: -/// * `use_mulithop` was not present in the settings +/// * `use_multihop` was not present in the settings /// * A multihop entry location had been previously specified. /// /// It is also no longer valid to have `entry_location` set to null. So remove the field if it @@ -72,6 +72,9 @@ pub fn migrate(settings: &mut serde_json::Value) -> Result<Option<MigrationData> if !version_matches(settings) { return Ok(None); } + + log::info!("Migrating settings format to V6"); + if let Some(wireguard_constraints) = get_wireguard_constraints(settings) { if let Some(location) = wireguard_constraints.get("entry_location") { if wireguard_constraints.get("use_multihop").is_none() { diff --git a/mullvad-daemon/src/migrations/v6.rs b/mullvad-daemon/src/migrations/v6.rs new file mode 100644 index 0000000000..3fa6da7d43 --- /dev/null +++ b/mullvad-daemon/src/migrations/v6.rs @@ -0,0 +1,305 @@ +use super::{Error, Result}; +use mullvad_types::settings::SettingsVersion; + +// ====================================================== +// Section for vendoring types and values that +// this settings version depend on. See `mod.rs`. + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +#[serde(rename_all = "lowercase")] +pub enum QuantumResistantState { + Auto, + On, + Off, +} + +// ====================================================== + +/// This is an open ended migration. There is no v7 yet! +/// The migrations performed by this function are still backwards compatible. +/// The JSON coming out of this migration can be read by any v6 compatible daemon. +/// +/// When further migrations are needed, add them here and if they are not backwards +/// compatible then create v7 and "close" this migration for further modification. +/// +/// The `use_pq_safe_psk` tunnel option is replaced by `quantum_resistant`, which +/// is optional. `false` is mapped to `None`. `true` is mapped to `Some(true)`. +pub fn migrate(settings: &mut serde_json::Value) -> Result<()> { + if !version_matches(settings) { + return Ok(()); + } + + migrate_pq_setting(settings)?; + + // TODO + // log::info!("Migrating settings format to V7"); + + // Note: Not incrementing the version number yet, since this migration is still open + // for future modification. + // settings["settings_version"] = serde_json::json!(SettingsVersion::V7); + + Ok(()) +} + +fn migrate_pq_setting(settings: &mut serde_json::Value) -> Result<()> { + if let Some(tunnel_options) = settings + .get_mut("tunnel_options") + .and_then(|opt| opt.get_mut("wireguard")) + { + if let Some(psk_setting) = tunnel_options + .as_object_mut() + .ok_or(Error::InvalidSettingsContent)? + .remove("use_pq_safe_psk") + { + if let Some(true) = psk_setting.as_bool() { + tunnel_options["quantum_resistant"] = serde_json::json!(QuantumResistantState::On); + } else { + tunnel_options["quantum_resistant"] = + serde_json::json!(QuantumResistantState::Auto); + } + } + } + Ok(()) +} + +fn version_matches(settings: &mut serde_json::Value) -> bool { + settings + .get("settings_version") + .map(|version| version == SettingsVersion::V6 as u64) + .unwrap_or(false) +} + +#[cfg(test)] +mod test { + use super::{migrate, migrate_pq_setting, version_matches}; + use serde_json; + + pub const V6_SETTINGS: &str = r#" +{ + "relay_settings": { + "normal": { + "location": { + "only": { + "country": "se" + } + }, + "tunnel_protocol": "any", + "wireguard_constraints": { + "port": "any", + "ip_version": "any", + "use_multihop": true, + "entry_location": "any" + }, + "openvpn_constraints": { + "port": { + "only": { + "protocol": "udp", + "port": { + "only": 1195 + } + } + } + } + } + }, + "bridge_settings": { + "normal": { + "location": "any" + } + }, + "obfuscation_settings": { + "selected_obfuscation": "udp2_tcp", + "udp2tcp": { + "port": "any" + } + }, + "bridge_state": "auto", + "allow_lan": true, + "block_when_disconnected": false, + "auto_connect": false, + "tunnel_options": { + "openvpn": { + "mssfix": null + }, + "wireguard": { + "mtu": null, + "rotation_interval": { + "secs": 86400, + "nanos": 0 + }, + "use_pq_safe_psk": false + }, + "generic": { + "enable_ipv6": false + }, + "dns_options": { + "state": "default", + "default_options": { + "block_ads": false, + "block_trackers": false + }, + "custom_options": { + "addresses": [ + "1.1.1.1", + "1.2.3.4" + ] + } + } + }, + "settings_version": 6 +} +"#; + + pub const V7_SETTINGS: &str = r#" +{ + "relay_settings": { + "normal": { + "location": { + "only": { + "country": "se" + } + }, + "tunnel_protocol": "any", + "wireguard_constraints": { + "port": "any", + "ip_version": "any", + "use_multihop": true, + "entry_location": "any" + }, + "openvpn_constraints": { + "port": { + "only": { + "protocol": "udp", + "port": { + "only": 1195 + } + } + } + } + } + }, + "bridge_settings": { + "normal": { + "location": "any" + } + }, + "obfuscation_settings": { + "selected_obfuscation": "udp2_tcp", + "udp2tcp": { + "port": "any" + } + }, + "bridge_state": "auto", + "allow_lan": true, + "block_when_disconnected": false, + "auto_connect": false, + "tunnel_options": { + "openvpn": { + "mssfix": null + }, + "wireguard": { + "mtu": null, + "rotation_interval": { + "secs": 86400, + "nanos": 0 + }, + "quantum_resistant": "auto" + }, + "generic": { + "enable_ipv6": false + }, + "dns_options": { + "state": "default", + "default_options": { + "block_ads": false, + "block_trackers": false + }, + "custom_options": { + "addresses": [ + "1.1.1.1", + "1.2.3.4" + ] + } + } + }, + "settings_version": 6 +} +"#; + + #[test] + fn test_v6_to_v7_migration() { + let mut old_settings = serde_json::from_str(V6_SETTINGS).unwrap(); + + assert!(version_matches(&mut old_settings)); + migrate(&mut old_settings).unwrap(); + let new_settings: serde_json::Value = serde_json::from_str(V7_SETTINGS).unwrap(); + + assert_eq!(&old_settings, &new_settings); + } + + /// use_pq_safe_psk=false should be replaced with quantum_resistant=null + #[test] + fn test_from_pq_safe_psk_false() { + let mut migrated_settings: serde_json::Value = serde_json::from_str( + r#" + { + "tunnel_options": { + "wireguard": { + "use_pq_safe_psk": false + } + } + } + "#, + ) + .unwrap(); + migrate_pq_setting(&mut migrated_settings).unwrap(); + + let expected_settings: serde_json::Value = serde_json::from_str( + r#" + { + "tunnel_options": { + "wireguard": { + "quantum_resistant": "auto" + } + } + } + "#, + ) + .unwrap(); + + assert_eq!(migrated_settings, expected_settings); + } + + /// use_pq_safe_psk=true should be replaced with quantum_resistant=true + #[test] + fn test_from_pq_safe_psk_true() { + let mut migrated_settings: serde_json::Value = serde_json::from_str( + r#" + { + "tunnel_options": { + "wireguard": { + "use_pq_safe_psk": true + } + } + } + "#, + ) + .unwrap(); + migrate_pq_setting(&mut migrated_settings).unwrap(); + + let expected_settings: serde_json::Value = serde_json::from_str( + r#" + { + "tunnel_options": { + "wireguard": { + "quantum_resistant": "on" + } + } + } + "#, + ) + .unwrap(); + + assert_eq!(migrated_settings, expected_settings); + } +} diff --git a/mullvad-daemon/src/settings.rs b/mullvad-daemon/src/settings.rs index 3ca4408f06..2c1ea033f9 100644 --- a/mullvad-daemon/src/settings.rs +++ b/mullvad-daemon/src/settings.rs @@ -3,7 +3,7 @@ use futures::TryFutureExt; use mullvad_types::{ relay_constraints::{BridgeSettings, BridgeState, ObfuscationSettings, RelaySettingsUpdate}, settings::{DnsOptions, Settings}, - wireguard::RotationInterval, + wireguard::{QuantumResistantState, RotationInterval}, }; use rand::Rng; #[cfg(target_os = "windows")] @@ -223,16 +223,11 @@ impl SettingsPersister { pub async fn set_quantum_resistant_tunnel( &mut self, - use_pq_safe_psk: bool, + quantum_resistant: QuantumResistantState, ) -> Result<bool, Error> { let should_save = Self::update_field( - &mut self - .settings - .tunnel_options - .wireguard - .options - .use_pq_safe_psk, - use_pq_safe_psk, + &mut self.settings.tunnel_options.wireguard.quantum_resistant, + quantum_resistant, ); self.update(should_save).await } @@ -244,8 +239,7 @@ impl SettingsPersister { } pub async fn set_wireguard_mtu(&mut self, mtu: Option<u16>) -> Result<bool, Error> { - let should_save = - Self::update_field(&mut self.settings.tunnel_options.wireguard.options.mtu, mtu); + let should_save = Self::update_field(&mut self.settings.tunnel_options.wireguard.mtu, mtu); self.update(should_save).await } @@ -301,12 +295,7 @@ impl SettingsPersister { #[cfg(windows)] pub async fn set_use_wireguard_nt(&mut self, state: bool) -> Result<bool, Error> { let should_save = Self::update_field( - &mut self - .settings - .tunnel_options - .wireguard - .options - .use_wireguard_nt, + &mut self.settings.tunnel_options.wireguard.use_wireguard_nt, state, ); self.update(should_save).await diff --git a/mullvad-daemon/src/tunnel.rs b/mullvad-daemon/src/tunnel.rs index 7cf52a3c5a..8ee0f1e6d7 100644 --- a/mullvad-daemon/src/tunnel.rs +++ b/mullvad-daemon/src/tunnel.rs @@ -221,7 +221,11 @@ impl InnerParametersGenerator { #[cfg(target_os = "linux")] fwmark: Some(mullvad_types::TUNNEL_FWMARK), }, - options: self.tunnel_options.wireguard.options.clone(), + options: self + .tunnel_options + .wireguard + .clone() + .into_talpid_tunnel_options(), generic_options: self.tunnel_options.generic.clone(), obfuscation: obfuscator_config, } diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto index cd9141981c..a2212952b0 100644 --- a/mullvad-management-interface/proto/management_interface.proto +++ b/mullvad-management-interface/proto/management_interface.proto @@ -43,7 +43,7 @@ service ManagementService { rpc SetOpenvpnMssfix(google.protobuf.UInt32Value) returns (google.protobuf.Empty) {} rpc SetWireguardMtu(google.protobuf.UInt32Value) returns (google.protobuf.Empty) {} rpc SetEnableIpv6(google.protobuf.BoolValue) returns (google.protobuf.Empty) {} - rpc SetQuantumResistantTunnel(google.protobuf.BoolValue) returns (google.protobuf.Empty) {} + rpc SetQuantumResistantTunnel(QuantumResistantState) returns (google.protobuf.Empty) {} rpc SetDnsOptions(DnsOptions) returns (google.protobuf.Empty) {} // Account management @@ -437,6 +437,15 @@ message ConnectionConfig { } } +message QuantumResistantState { + enum State { + AUTO = 0; + ON = 1; + OFF = 2; + } + State state = 1; +} + message TunnelOptions { message OpenvpnOptions { uint32 mssfix = 1; @@ -445,7 +454,7 @@ message TunnelOptions { uint32 mtu = 1; google.protobuf.Duration rotation_interval = 2; bool use_wireguard_nt = 3; - bool use_pq_safe_psk = 4; + QuantumResistantState quantum_resistant = 4; } message GenericOptions { bool enable_ipv6 = 1; diff --git a/mullvad-management-interface/src/types/conversions/settings.rs b/mullvad-management-interface/src/types/conversions/settings.rs index 60805d5004..8a97c3b213 100644 --- a/mullvad-management-interface/src/types/conversions/settings.rs +++ b/mullvad-management-interface/src/types/conversions/settings.rs @@ -77,16 +77,16 @@ impl From<&mullvad_types::settings::TunnelOptions> for proto::TunnelOptions { mssfix: u32::from(options.openvpn.mssfix.unwrap_or_default()), }), wireguard: Some(proto::tunnel_options::WireguardOptions { - mtu: u32::from(options.wireguard.options.mtu.unwrap_or_default()), + mtu: u32::from(options.wireguard.mtu.unwrap_or_default()), rotation_interval: options.wireguard.rotation_interval.map(|ivl| { prost_types::Duration::try_from(std::time::Duration::from(ivl)) .expect("Failed to convert std::time::Duration to prost_types::Duration for tunnel_options.wireguard.rotation_interval") }), #[cfg(windows)] - use_wireguard_nt: options.wireguard.options.use_wireguard_nt, + use_wireguard_nt: options.wireguard.use_wireguard_nt, #[cfg(not(windows))] use_wireguard_nt: false, - use_pq_safe_psk: options.wireguard.options.use_pq_safe_psk, + quantum_resistant: Some(proto::QuantumResistantState::from(options.wireguard.quantum_resistant)), }), generic: Some(proto::tunnel_options::GenericOptions { enable_ipv6: options.generic.enable_ipv6, @@ -113,7 +113,7 @@ impl TryFrom<proto::TunnelOptions> for mullvad_types::settings::TunnelOptions { let wireguard_options = options .wireguard .ok_or(FromProtobufTypeError::InvalidArgument( - "missing openvpn tunnel options", + "missing wireguard tunnel options", ))?; let generic_options = options .generic @@ -135,16 +135,13 @@ impl TryFrom<proto::TunnelOptions> for mullvad_types::settings::TunnelOptions { }, }, wireguard: mullvad_types::wireguard::TunnelOptions { - options: net::wireguard::TunnelOptions { - mtu: if wireguard_options.mtu != 0 { - Some(wireguard_options.mtu as u16) - } else { - None - }, - use_pq_safe_psk: wireguard_options.use_pq_safe_psk, - #[cfg(windows)] - use_wireguard_nt: wireguard_options.use_wireguard_nt, + mtu: if wireguard_options.mtu != 0 { + Some(wireguard_options.mtu as u16) + } else { + None }, + #[cfg(windows)] + use_wireguard_nt: wireguard_options.use_wireguard_nt, rotation_interval: wireguard_options .rotation_interval .map(std::time::Duration::try_from) @@ -159,6 +156,12 @@ impl TryFrom<proto::TunnelOptions> for mullvad_types::settings::TunnelOptions { ); FromProtobufTypeError::InvalidArgument("invalid rotation interval") })?, + quantum_resistant: wireguard_options + .quantum_resistant + .map(mullvad_types::wireguard::QuantumResistantState::try_from) + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing quantum resistant state", + ))??, }, generic: net::GenericTunnelOptions { enable_ipv6: generic_options.enable_ipv6, diff --git a/mullvad-management-interface/src/types/conversions/wireguard.rs b/mullvad-management-interface/src/types/conversions/wireguard.rs index e90e728c2f..ce2d4c7dba 100644 --- a/mullvad-management-interface/src/types/conversions/wireguard.rs +++ b/mullvad-management-interface/src/types/conversions/wireguard.rs @@ -1,3 +1,4 @@ +use super::FromProtobufTypeError; use crate::types::proto; use prost_types::Timestamp; @@ -12,3 +13,40 @@ impl From<mullvad_types::wireguard::PublicKey> for proto::PublicKey { } } } + +impl From<mullvad_types::wireguard::QuantumResistantState> for proto::QuantumResistantState { + fn from(state: mullvad_types::wireguard::QuantumResistantState) -> Self { + match state { + mullvad_types::wireguard::QuantumResistantState::Auto => proto::QuantumResistantState { + state: i32::from(proto::quantum_resistant_state::State::Auto), + }, + mullvad_types::wireguard::QuantumResistantState::On => proto::QuantumResistantState { + state: i32::from(proto::quantum_resistant_state::State::On), + }, + mullvad_types::wireguard::QuantumResistantState::Off => proto::QuantumResistantState { + state: i32::from(proto::quantum_resistant_state::State::Off), + }, + } + } +} + +impl TryFrom<proto::QuantumResistantState> for mullvad_types::wireguard::QuantumResistantState { + type Error = FromProtobufTypeError; + + fn try_from(state: proto::QuantumResistantState) -> Result<Self, Self::Error> { + match proto::quantum_resistant_state::State::from_i32(state.state) { + Some(proto::quantum_resistant_state::State::Auto) => { + Ok(mullvad_types::wireguard::QuantumResistantState::Auto) + } + Some(proto::quantum_resistant_state::State::On) => { + Ok(mullvad_types::wireguard::QuantumResistantState::On) + } + Some(proto::quantum_resistant_state::State::Off) => { + Ok(mullvad_types::wireguard::QuantumResistantState::Off) + } + None => Err(FromProtobufTypeError::InvalidArgument( + "invalid quantum resistance state", + )), + } + } +} diff --git a/mullvad-types/src/custom_tunnel.rs b/mullvad-types/src/custom_tunnel.rs index 2dceb7493d..a3f8c0b264 100644 --- a/mullvad-types/src/custom_tunnel.rs +++ b/mullvad-types/src/custom_tunnel.rs @@ -51,7 +51,7 @@ impl CustomTunnelEndpoint { let parameters = match config { ConnectionConfig::OpenVpn(config) => openvpn::TunnelParameters { config, - options: tunnel_options.openvpn.clone(), + options: tunnel_options.openvpn, generic_options: tunnel_options.generic, proxy, #[cfg(target_os = "linux")] @@ -60,7 +60,7 @@ impl CustomTunnelEndpoint { .into(), ConnectionConfig::Wireguard(connection) => wireguard::TunnelParameters { connection, - options: tunnel_options.wireguard.options.clone(), + options: tunnel_options.wireguard.into_talpid_tunnel_options(), generic_options: tunnel_options.generic, obfuscation: None, } diff --git a/mullvad-types/src/settings/mod.rs b/mullvad-types/src/settings/mod.rs index d6949ab0bb..9b88baebc9 100644 --- a/mullvad-types/src/settings/mod.rs +++ b/mullvad-types/src/settings/mod.rs @@ -12,7 +12,7 @@ use rand::Rng; use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(target_os = "windows")] use std::{collections::HashSet, path::PathBuf}; -use talpid_types::net::{self, openvpn, GenericTunnelOptions}; +use talpid_types::net::{openvpn, GenericTunnelOptions}; mod dns; @@ -211,10 +211,7 @@ impl Default for TunnelOptions { fn default() -> Self { TunnelOptions { openvpn: openvpn::TunnelOptions::default(), - wireguard: wireguard::TunnelOptions { - options: net::wireguard::TunnelOptions::default(), - rotation_interval: None, - }, + wireguard: wireguard::TunnelOptions::default(), generic: GenericTunnelOptions { // Enable IPv6 be default on Android enable_ipv6: cfg!(target_os = "android"), diff --git a/mullvad-types/src/wireguard.rs b/mullvad-types/src/wireguard.rs index fac2adf0d3..5f77d769eb 100644 --- a/mullvad-types/src/wireguard.rs +++ b/mullvad-types/src/wireguard.rs @@ -14,6 +14,18 @@ pub const DEFAULT_ROTATION_INTERVAL: Duration = if cfg!(target_os = "android") { Duration::from_secs(7 * 24 * 60 * 60) }; +/// Whether to enable or disable quantum resistant tunnels when the setting +/// is set to `QuantumResistantState::Auto`. +const QUANTUM_RESISTANT_AUTO_STATE: bool = false; + +#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq)] +#[serde(rename_all = "lowercase")] +pub enum QuantumResistantState { + Auto, + On, + Off, +} + /// Contains account specific wireguard data #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct WireguardData { @@ -114,7 +126,7 @@ impl Default for RotationInterval { } } -#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(default)] #[cfg_attr(target_os = "android", derive(IntoJava))] #[cfg_attr( @@ -122,13 +134,59 @@ impl Default for RotationInterval { jnix(class_name = "net.mullvad.mullvadvpn.model.WireguardTunnelOptions") )] pub struct TunnelOptions { - #[serde(flatten)] - pub options: wireguard::TunnelOptions, + /// MTU for the wireguard tunnel + #[cfg_attr( + target_os = "android", + jnix(map = "|maybe_mtu| maybe_mtu.map(|mtu| mtu as i32)") + )] + pub mtu: Option<u16>, + /// Temporary switch for wireguard-nt + #[cfg(windows)] + #[serde(rename = "wireguard_nt")] + pub use_wireguard_nt: bool, + /// Obtain a PSK using the relay config client. + #[cfg_attr( + target_os = "android", + jnix(map = "|state| match state { + QuantumResistantState::Auto => None, + QuantumResistantState::On => Some(true), + QuantumResistantState::Off => Some(false), + }") + )] + pub quantum_resistant: QuantumResistantState, /// Interval used for automatic key rotation #[cfg_attr(target_os = "android", jnix(skip))] pub rotation_interval: Option<RotationInterval>, } +#[allow(clippy::derivable_impls)] +impl Default for TunnelOptions { + fn default() -> Self { + TunnelOptions { + mtu: None, + quantum_resistant: QuantumResistantState::Auto, + #[cfg(windows)] + use_wireguard_nt: true, + rotation_interval: None, + } + } +} + +impl TunnelOptions { + pub fn into_talpid_tunnel_options(self) -> wireguard::TunnelOptions { + wireguard::TunnelOptions { + mtu: self.mtu, + #[cfg(windows)] + use_wireguard_nt: self.use_wireguard_nt, + quantum_resistant: match self.quantum_resistant { + QuantumResistantState::Auto => QUANTUM_RESISTANT_AUTO_STATE, + QuantumResistantState::On => true, + QuantumResistantState::Off => false, + }, + } + } +} + /// Represents a published public key #[derive(Serialize, Deserialize, Clone, Debug)] #[cfg_attr(target_os = "android", derive(IntoJava))] diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs index a8d5792d5b..29f3f9c746 100644 --- a/talpid-core/src/tunnel/mod.rs +++ b/talpid-core/src/tunnel/mod.rs @@ -143,7 +143,7 @@ impl TunnelMonitor { let config = talpid_wireguard::config::Config::from_parameters(params)?; let monitor = talpid_wireguard::WireguardMonitor::start( config, - if params.options.use_pq_safe_psk { + if params.options.quantum_resistant { Some( params .connection diff --git a/talpid-types/src/net/mod.rs b/talpid-types/src/net/mod.rs index bcfe6f7b3c..b957e59a34 100644 --- a/talpid-types/src/net/mod.rs +++ b/talpid-types/src/net/mod.rs @@ -18,7 +18,7 @@ pub mod wireguard; /// TunnelParameters are used to encapsulate all the data needed to start a tunnel. This is enum /// should be generated by implementations of the trait /// `talpid-core::tunnel_state_machine::TunnelParametersGenerator` -#[derive(Clone, Eq, PartialEq, Deserialize, Serialize, Debug)] +#[derive(Clone, Eq, PartialEq, Debug)] pub enum TunnelParameters { OpenVpn(openvpn::TunnelParameters), Wireguard(wireguard::TunnelParameters), @@ -37,7 +37,7 @@ impl TunnelParameters { }, TunnelParameters::Wireguard(params) => TunnelEndpoint { tunnel_type: TunnelType::Wireguard, - quantum_resistant: params.options.use_pq_safe_psk, + quantum_resistant: params.options.quantum_resistant, endpoint: params .connection .get_exit_endpoint() diff --git a/talpid-types/src/net/wireguard.rs b/talpid-types/src/net/wireguard.rs index 8d84ef5a73..181e647cf7 100644 --- a/talpid-types/src/net/wireguard.rs +++ b/talpid-types/src/net/wireguard.rs @@ -1,7 +1,5 @@ use crate::net::{Endpoint, GenericTunnelOptions, TransportProtocol}; use ipnetwork::IpNetwork; -#[cfg(target_os = "android")] -use jnix::IntoJava; use rand::rngs::OsRng; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::{ @@ -13,7 +11,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop}; /// Tunnel parameters required to start a `WireguardMonitor`. /// See [`crate::net::TunnelParameters`]. -#[derive(Clone, Eq, PartialEq, Deserialize, Serialize, Debug)] +#[derive(Clone, Eq, PartialEq, Debug)] pub struct TunnelParameters { pub connection: ConnectionConfig, pub options: TunnelOptions, @@ -73,44 +71,15 @@ pub struct TunnelConfig { } /// Options in [`TunnelParameters`] that apply to any WireGuard connection. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(default)] -#[cfg_attr(target_os = "android", derive(IntoJava))] -#[cfg_attr( - target_os = "android", - jnix(package = "net.mullvad.talpid.net.wireguard") -)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct TunnelOptions { /// MTU for the wireguard tunnel - #[cfg_attr( - target_os = "android", - jnix(map = "|maybe_mtu| maybe_mtu.map(|mtu| mtu as i32)") - )] pub mtu: Option<u16>, - /// Obtain a PSK using the relay config client. - pub use_pq_safe_psk: bool, /// Temporary switch for wireguard-nt #[cfg(windows)] - #[serde(default = "default_wgnt_setting")] - #[serde(rename = "wireguard_nt")] pub use_wireguard_nt: bool, -} - -#[cfg(windows)] -fn default_wgnt_setting() -> bool { - true -} - -#[allow(clippy::derivable_impls)] -impl Default for TunnelOptions { - fn default() -> Self { - Self { - mtu: None, - use_pq_safe_psk: false, - #[cfg(windows)] - use_wireguard_nt: default_wgnt_setting(), - } - } + /// Perform PQ-safe PSK exchange when connecting + pub quantum_resistant: bool, } /// Wireguard x25519 private key |
