summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock13
-rw-r--r--gui/src/main/daemon-rpc.ts8
-rw-r--r--gui/src/main/index.ts4
-rw-r--r--gui/src/shared/daemon-rpc-types.ts9
-rw-r--r--mullvad-cli/src/cmds/status.rs5
-rw-r--r--mullvad-cli/src/cmds/tunnel.rs6
-rw-r--r--mullvad-daemon/Cargo.toml2
-rw-r--r--mullvad-daemon/src/lib.rs181
-rw-r--r--mullvad-daemon/src/management_interface.rs19
-rw-r--r--mullvad-daemon/src/wireguard.rs234
-rw-r--r--mullvad-ipc-client/src/lib.rs2
-rw-r--r--mullvad-jni/src/daemon_interface.rs8
-rw-r--r--mullvad-jni/src/jni_event_listener.rs5
-rw-r--r--mullvad-jni/src/lib.rs8
-rw-r--r--mullvad-types/src/lib.rs3
-rw-r--r--mullvad-types/src/wireguard.rs20
16 files changed, 474 insertions, 53 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f98147de1c..6ed1e941b0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1112,6 +1112,7 @@ dependencies = [
"fern 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
"ipnetwork 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "jsonrpc-client-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 8.0.2 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
"jsonrpc-ipc-server 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
"jsonrpc-macros 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
@@ -1133,6 +1134,7 @@ dependencies = [
"talpid-ipc 0.1.0",
"talpid-types 0.1.0",
"tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-retry 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2406,6 +2408,16 @@ dependencies = [
]
[[package]]
+name = "tokio-retry"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "tokio-service"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2992,6 +3004,7 @@ dependencies = [
"checksum tokio-process 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88e1281e412013f1ff5787def044a9577a0bed059f451e835f1643201f8b777d"
"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389"
"checksum tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6af16bfac7e112bea8b0442542161bfc41cbfa4466b580bdda7d18cb88b911ce"
+"checksum tokio-retry 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c03755b956458582182941061def32b8123a26c98b08fc6ddcf49ae89d18f33"
"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"
"checksum tokio-signal 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "dd6dc5276ea05ce379a16de90083ec80836440d5ef8a6a39545a3207373b8296"
"checksum tokio-sync 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fda385df506bf7546e70872767f71e81640f1f251bdf2fd8eb81a0eaec5fe022"
diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts
index f50428ea8a..215807b2a7 100644
--- a/gui/src/main/daemon-rpc.ts
+++ b/gui/src/main/daemon-rpc.ts
@@ -337,6 +337,14 @@ const daemonEventSchema = oneOf(
object({
relay_list: relayListSchema,
}),
+ object({
+ wireguard_key: oneOf(
+ enumeration('too_many_keys', 'generation_failure'),
+ object({
+ new_key: string,
+ }),
+ ),
+ }),
);
export class ResponseParseError extends Error {
diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts
index 15a137dd10..b086b97d08 100644
--- a/gui/src/main/index.ts
+++ b/gui/src/main/index.ts
@@ -486,6 +486,10 @@ class ApplicationMain {
this.setSettings(daemonEvent.settings);
} else if ('relayList' in daemonEvent) {
this.setRelays(daemonEvent.relayList, this.settings.relaySettings);
+ } else if ('wireguardKey' in daemonEvent) {
+ /// TODO: handle wireguard key events properly.
+ log.info(`Received new key event`);
+ log.info(daemonEvent);
}
},
(error: Error) => {
diff --git a/gui/src/shared/daemon-rpc-types.ts b/gui/src/shared/daemon-rpc-types.ts
index 32cd6c4d0f..120ef3a0b0 100644
--- a/gui/src/shared/daemon-rpc-types.ts
+++ b/gui/src/shared/daemon-rpc-types.ts
@@ -74,7 +74,8 @@ export interface IProxyEndpoint {
export type DaemonEvent =
| { stateTransition: TunnelStateTransition }
| { settings: ISettings }
- | { relayList: IRelayList };
+ | { relayList: IRelayList }
+ | { wireguardKey: KeygenEvent };
export type TunnelStateTransition =
| { state: 'disconnected' }
@@ -278,6 +279,12 @@ export interface ISettings {
bridgeState: BridgeState;
}
+export type KeygenEvent = INewWireguardKey | 'too_many_keys' | 'generation_failure';
+
+export interface INewWireguardKey {
+ newKey: string;
+}
+
export type BridgeState = 'auto' | 'on' | 'off';
export interface IBridgeConstraints {
diff --git a/mullvad-cli/src/cmds/status.rs b/mullvad-cli/src/cmds/status.rs
index 8642c581ce..4846296fbb 100644
--- a/mullvad-cli/src/cmds/status.rs
+++ b/mullvad-cli/src/cmds/status.rs
@@ -57,6 +57,11 @@ impl Command for Status {
println!("New relay list: {:#?}", relay_list);
}
}
+ DaemonEvent::WireguardKey(key_event) => {
+ if verbose {
+ println!("{}", key_event);
+ }
+ }
}
}
}
diff --git a/mullvad-cli/src/cmds/tunnel.rs b/mullvad-cli/src/cmds/tunnel.rs
index 7eacdddb78..9fc7c12469 100644
--- a/mullvad-cli/src/cmds/tunnel.rs
+++ b/mullvad-cli/src/cmds/tunnel.rs
@@ -173,7 +173,11 @@ impl Tunnel {
fn process_wireguard_key_generate() -> Result<()> {
let mut rpc = new_rpc_client()?;
- rpc.generate_wireguard_key().map_err(|e| e.into())
+ let result = rpc
+ .generate_wireguard_key()
+ .map_err(|e| crate::Error::RpcClientError(e))?;
+ println!("{}", result);
+ Ok(())
}
fn handle_ipv6_cmd(matches: &clap::ArgMatches<'_>) -> Result<()> {
diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml
index 84abf172a8..417e2bc408 100644
--- a/mullvad-daemon/Cargo.toml
+++ b/mullvad-daemon/Cargo.toml
@@ -32,6 +32,8 @@ uuid = { version = "0.7", features = ["v4"] }
lazy_static = "1.0"
rand = "0.6"
tokio-core = "0.1"
+tokio-retry = "0.2"
+jsonrpc-client-core = "0.5"
tokio-timer = "0.1"
regex = "1.0"
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index b3aa513f6e..7a559624ec 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -26,7 +26,7 @@ use crate::management_interface::{
BoxFuture, ManagementInterfaceEventBroadcaster, ManagementInterfaceServer,
};
use futures::{
- future,
+ future::{self, Executor},
sync::{mpsc::UnboundedSender, oneshot},
Future, Sink,
};
@@ -43,6 +43,7 @@ use mullvad_types::{
relay_list::{Relay, RelayList},
states::TargetState,
version::{AppVersion, AppVersionInfo},
+ wireguard::KeygenEvent,
};
use settings::Settings;
use std::{io, mem, path::PathBuf, sync::mpsc, thread, time::Duration};
@@ -52,11 +53,14 @@ use talpid_core::{
tunnel_state_machine::{self, TunnelCommand, TunnelParametersGenerator},
};
use talpid_types::{
- net::{openvpn, wireguard, TransportProtocol, TunnelParameters},
+ net::{openvpn, TransportProtocol, TunnelParameters},
tunnel::{BlockReason, TunnelStateTransition},
ErrorExt,
};
+#[path = "wireguard.rs"]
+mod wireguard;
+
pub type Result<T> = std::result::Result<T, Error>;
#[derive(err_derive::Error, Debug)]
@@ -102,7 +106,7 @@ pub enum Error {
type SyncUnboundedSender<T> = ::futures::sink::Wait<UnboundedSender<T>>;
/// All events that can happen in the daemon. Sent from various threads and exposed interfaces.
-enum InternalDaemonEvent {
+pub(crate) enum InternalDaemonEvent {
/// Tunnel has changed state.
TunnelStateTransition(TunnelStateTransition),
/// Request from the `MullvadTunnelParametersGenerator` to obtain a new relay.
@@ -113,6 +117,13 @@ enum InternalDaemonEvent {
ManagementInterfaceExited,
/// Daemon shutdown triggered by a signal, ctrl-c or similar.
TriggerShutdown,
+ /// Wireguard key generation event
+ WgKeyEvent(
+ (
+ AccountToken,
+ ::std::result::Result<mullvad_types::wireguard::WireguardData, wireguard::Error>,
+ ),
+ ),
}
impl From<TunnelStateTransition> for InternalDaemonEvent {
@@ -192,6 +203,9 @@ pub trait EventListener {
/// Notify that the relay list changed.
fn notify_relay_list(&self, relay_list: RelayList);
+
+ /// Notify clients of a key generation event.
+ fn notify_key_event(&self, key_event: KeygenEvent);
}
pub struct Daemon<L: EventListener = ManagementInterfaceEventBroadcaster> {
@@ -209,6 +223,7 @@ pub struct Daemon<L: EventListener = ManagementInterfaceEventBroadcaster> {
accounts_proxy: AccountsProxy<HttpHandle>,
version_proxy: AppVersionProxy<HttpHandle>,
https_handle: mullvad_rpc::rest::RequestSender,
+ wireguard_key_manager: wireguard::KeyManager,
tokio_remote: tokio_core::reactor::Remote,
relay_selector: relays::RelaySelector,
last_generated_relay: Option<Relay>,
@@ -324,6 +339,7 @@ where
(rpc, https_handle, remote)
})
.map_err(Error::InitIoEventLoop)?;
+
let rpc_handle = rpc_handle.map_err(Error::InitRpcClient)?;
let https_handle = https_handle.map_err(Error::InitHttpsClient)?;
@@ -332,6 +348,7 @@ where
relay_list_listener.notify_relay_list(relay_list.clone());
};
+
let relay_selector = relays::RelaySelector::new(
rpc_handle.clone(),
on_relay_list_update,
@@ -359,10 +376,17 @@ where
)
.map_err(Error::TunnelError)?;
+
+ let wireguard_key_manager = wireguard::KeyManager::new(
+ internal_event_tx.clone(),
+ rpc_handle.clone(),
+ tokio_remote.clone(),
+ );
+
// Attempt to download a fresh relay list
relay_selector.update();
- Ok(Daemon {
+ let mut daemon = Daemon {
tunnel_command_tx: Sink::wait(tunnel_command_tx),
tunnel_state: TunnelStateTransition::Disconnected,
target_state: TargetState::Unsecured,
@@ -382,7 +406,12 @@ where
last_generated_relay: None,
last_generated_bridge_relay: None,
version,
- })
+ wireguard_key_manager,
+ };
+
+ daemon.ensure_wireguard_keys_for_current_account();
+
+ Ok(daemon)
}
/// Retrieve a channel for sending daemon commands.
@@ -390,6 +419,7 @@ where
DaemonCommandSender::new(self.tx.clone())
}
+
/// Consume the `Daemon` and run the main event loop. Blocks until an error happens or a
/// shutdown event is received.
pub fn run(mut self) -> Result<()> {
@@ -418,6 +448,7 @@ where
return Err(Error::ManagementInterfaceExited);
}
TriggerShutdown => self.handle_trigger_shutdown_event(),
+ WgKeyEvent(key_event) => self.handle_wireguard_key_event(key_event),
}
Ok(())
}
@@ -658,6 +689,70 @@ where
}
}
+ fn handle_wireguard_key_event(
+ &mut self,
+ event: (
+ AccountToken,
+ ::std::result::Result<mullvad_types::wireguard::WireguardData, wireguard::Error>,
+ ),
+ ) {
+ let (account, result) = event;
+ // If the account has been reset whilst a key was being generated, the event should be
+ // dropped even if a new key was generated.
+ if self
+ .settings
+ .get_account_token()
+ .map(|current_account| current_account != account)
+ .unwrap_or(true)
+ {
+ log::info!("Dropping wireguard key event since account has been changed");
+ return;
+ }
+
+ match result {
+ Ok(data) => {
+ let public_key = data.private_key.public_key();
+ let mut account_entry = self
+ .account_history
+ .get(&account)
+ .ok()
+ .and_then(|entry| entry)
+ .unwrap_or_else(|| account_history::AccountEntry {
+ account: account.clone(),
+ wireguard: None,
+ });
+ account_entry.wireguard = Some(data.clone());
+ match self.account_history.insert(account_entry) {
+ Ok(_) => self
+ .event_listener
+ .notify_key_event(KeygenEvent::NewKey(public_key)),
+ Err(e) => {
+ log::error!(
+ "{}",
+ e.display_chain_with_msg(
+ "Failed to add new wireguard key to account data"
+ )
+ );
+ self.event_listener
+ .notify_key_event(KeygenEvent::GenerationFailure)
+ }
+ }
+ }
+ Err(wireguard::Error::TooManyKeys) => {
+ self.event_listener
+ .notify_key_event(KeygenEvent::TooManyKeys);
+ }
+ Err(e) => {
+ log::error!(
+ "{}",
+ e.display_chain_with_msg("Failed to generate wireguard key")
+ );
+ self.event_listener
+ .notify_key_event(KeygenEvent::GenerationFailure);
+ }
+ }
+ }
+
fn on_set_target_state(
&mut self,
tx: oneshot::Sender<::std::result::Result<(), ()>>,
@@ -766,6 +861,7 @@ where
Ok(account_changed) => {
Self::oneshot_send(tx, (), "set_account response");
if account_changed {
+ self.ensure_wireguard_keys_for_current_account();
self.event_listener.notify_settings(self.settings.clone());
match account_token {
Some(token) => {
@@ -785,7 +881,6 @@ where
Err(e) => error!("{}", e.display_chain_with_msg("Unable to save settings")),
}
}
-
fn on_get_account_history(&mut self, tx: oneshot::Sender<Vec<AccountToken>>) {
Self::oneshot_send(
tx,
@@ -1013,11 +1108,32 @@ where
}
}
- fn on_generate_wireguard_key(
- &mut self,
- tx: oneshot::Sender<::std::result::Result<(), mullvad_rpc::Error>>,
- ) {
- let mut result = || -> ::std::result::Result<(), String> {
+ fn ensure_wireguard_keys_for_current_account(&mut self) {
+ if let Some(account) = self.settings.get_account_token() {
+ if self
+ .account_history
+ .get(&account)
+ .map(|entry| entry.map(|e| e.wireguard.is_none()).unwrap_or(true))
+ .unwrap_or(true)
+ {
+ log::info!("Autoamtically generating new wireguard key for account");
+ if let Err(e) = self
+ .wireguard_key_manager
+ .generate_key_async(account.to_owned())
+ {
+ log::error!(
+ "{}",
+ e.display_chain_with_msg("Failed to start generating wireguard key")
+ );
+ }
+ } else {
+ log::info!("Account already has wireguard key");
+ }
+ }
+ }
+
+ fn on_generate_wireguard_key(&mut self, tx: oneshot::Sender<KeygenEvent>) {
+ let mut result = || -> ::std::result::Result<KeygenEvent, String> {
let account_token = self
.settings
.get_account_token()
@@ -1037,32 +1153,26 @@ where
})
})?;
- let private_key = wireguard::PrivateKey::new_from_random()
- .map_err(|e| format!("Failed to generate new key - {}", e))?;
-
- let fut = self
- .wg_key_proxy
- .push_wg_key(account_token, private_key.public_key());
-
- let mut core = tokio_core::reactor::Core::new()
- .map_err(|e| format!("Failed to spawn future for pushing wg key - {}", e))?;
-
- let addresses = core
- .run(fut)
- .map_err(|e| format!("Failed to push new wireguard key: {}", e))?;
-
- account_entry.wireguard = Some(mullvad_types::wireguard::WireguardData {
- private_key,
- addresses,
- });
-
- self.account_history
- .insert(account_entry)
- .map_err(|e| format!("Failed to add new wireguard key to account data: {}", e))
+ match self
+ .wireguard_key_manager
+ .generate_key_sync(account_token.clone())
+ {
+ Ok(new_data) => {
+ let public_key = new_data.private_key.public_key();
+ account_entry.wireguard = Some(new_data.clone());
+ self.account_history.insert(account_entry).map_err(|e| {
+ format!("Failed to add new wireguard key to account data: {}", e)
+ })?;
+ Ok(KeygenEvent::NewKey(public_key))
+ }
+ Err(wireguard::Error::TooManyKeys) => Ok(KeygenEvent::TooManyKeys),
+ Err(e) => Err(format!("Failed to generate new key - {}", e)),
+ }
};
+
match result() {
- Ok(()) => {
- Self::oneshot_send(tx, Ok(()), "generate_wireguard_key response");
+ Ok(key_event) => {
+ Self::oneshot_send(tx, key_event, "generate_wireguard_key response");
}
Err(e) => {
log::error!("Failed to generate new wireguard key - {}", e);
@@ -1085,7 +1195,6 @@ where
}
fn on_verify_wireguard_key(&mut self, tx: oneshot::Sender<bool>) {
- use futures::future::Executor;
let account = match self.settings.get_account_token() {
Some(account) => account,
None => {
diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs
index 7c195d74e2..0ef34090d7 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -132,7 +132,7 @@ build_rpc_trait! {
/// Generates new wireguard key for current account
#[rpc(meta, name = "generate_wireguard_key")]
- fn generate_wireguard_key(&self, Self::Metadata) -> BoxFuture<(), Error>;
+ fn generate_wireguard_key(&self, Self::Metadata) -> BoxFuture<mullvad_types::wireguard::KeygenEvent, Error>;
/// Retrieve a public key for current account if the account has one.
#[rpc(meta, name = "get_wireguard_key")]
@@ -212,7 +212,7 @@ pub enum ManagementCommand {
/// Get the daemon settings
GetSettings(OneshotSender<Settings>),
/// Generate new wireguard key
- GenerateWireguardKey(OneshotSender<Result<(), mullvad_rpc::Error>>),
+ GenerateWireguardKey(OneshotSender<mullvad_types::wireguard::KeygenEvent>),
/// Return a public key of the currently set wireguard private key, if there is one
GetWireguardKey(OneshotSender<Option<wireguard::PublicKey>>),
/// Verify if the currently set wireguard key is valid.
@@ -296,6 +296,11 @@ impl EventListener for ManagementInterfaceEventBroadcaster {
log::debug!("Broadcasting new relay list");
self.notify(DaemonEvent::RelayList(relay_list));
}
+
+ fn notify_key_event(&self, key_event: mullvad_types::wireguard::KeygenEvent) {
+ log::debug!("Broadcasting new wireguard key event");
+ self.notify(DaemonEvent::WireguardKey(key_event));
+ }
}
impl ManagementInterfaceEventBroadcaster {
@@ -611,15 +616,15 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi
Box::new(future)
}
- fn generate_wireguard_key(&self, _: Self::Metadata) -> BoxFuture<(), Error> {
+ fn generate_wireguard_key(
+ &self,
+ _: Self::Metadata,
+ ) -> BoxFuture<mullvad_types::wireguard::KeygenEvent, Error> {
log::debug!("generate_wireguard_key");
let (tx, rx) = sync::oneshot::channel();
let future = self
.send_command_to_daemon(ManagementCommand::GenerateWireguardKey(tx))
- .and_then(|_| {
- rx.map_err(|_| Error::internal_error())
- .and_then(|res| future::result(res.map_err(|_| Error::internal_error())))
- });
+ .and_then(|_| rx.map_err(|_| Error::internal_error()));
Box::new(future)
}
diff --git a/mullvad-daemon/src/wireguard.rs b/mullvad-daemon/src/wireguard.rs
new file mode 100644
index 0000000000..11134c865e
--- /dev/null
+++ b/mullvad-daemon/src/wireguard.rs
@@ -0,0 +1,234 @@
+use crate::InternalDaemonEvent;
+use futures::{future::Executor, sync::oneshot, Async, Future, Poll};
+use jsonrpc_client_core::Error as JsonRpcError;
+use mullvad_types::{account::AccountToken, wireguard::WireguardData};
+use std::{sync::mpsc, time::Duration};
+pub use talpid_types::net::wireguard::*;
+use talpid_types::ErrorExt;
+use tokio_core::reactor::Remote;
+use tokio_retry::{
+ strategy::{jitter, ExponentialBackoff},
+ RetryIf,
+};
+
+const TOO_MANY_KEYS_ERROR_CODE: i64 = -703;
+
+
+#[derive(err_derive::Error, Debug)]
+pub enum Error {
+ #[error(display = "Failed to generate private key")]
+ GenerationError(#[error(cause)] rand::Error),
+ #[error(display = "Failed to spawn future")]
+ ExectuionError,
+ #[error(display = "Unexpected RPC error")]
+ RpcError(#[error(cause)] jsonrpc_client_core::Error),
+ #[error(display = "Account already has maximum number of keys")]
+ TooManyKeys,
+}
+
+pub type Result<T> = ::std::result::Result<T, Error>;
+
+pub struct KeyManager {
+ daemon_tx: mpsc::Sender<InternalDaemonEvent>,
+ http_handle: mullvad_rpc::HttpHandle,
+ tokio_remote: Remote,
+ current_job: Option<CancelHandle>,
+}
+
+impl KeyManager {
+ pub(crate) fn new(
+ daemon_tx: mpsc::Sender<InternalDaemonEvent>,
+ http_handle: mullvad_rpc::HttpHandle,
+ tokio_remote: Remote,
+ ) -> Self {
+ Self {
+ daemon_tx,
+ http_handle,
+ tokio_remote,
+ current_job: None,
+ }
+ }
+
+ /// Stop current key generation
+ pub fn reset(&mut self) {
+ if let Some(job) = self.current_job.take() {
+ job.cancel()
+ }
+ }
+
+ /// Generate a new private key
+ pub fn generate_key_sync(&mut self, account: AccountToken) -> Result<WireguardData> {
+ self.reset();
+ let private_key = PrivateKey::new_from_random().map_err(Error::GenerationError)?;
+ let (tx, rx) = oneshot::channel();
+ let fut = self.push_future_generator(account, private_key)().then(|result| {
+ let _ = tx.send(result);
+ Ok(())
+ });
+ self.tokio_remote
+ .execute(fut)
+ .map_err(|_e| Error::ExectuionError)?;
+
+ rx.wait()
+ .map_err(|_| Error::ExectuionError)?
+ .map_err(Self::map_rpc_error)
+ }
+
+
+ /// Generate a new private key asyncronously. The new keys will be sent to the daemon channel.
+ pub fn generate_key_async(&mut self, account: AccountToken) -> Result<()> {
+ self.reset();
+ let private_key = PrivateKey::new_from_random().map_err(Error::GenerationError)?;
+ let future_generator = self.push_future_generator(account.clone(), private_key);
+
+ let retry_strategy = ExponentialBackoff::from_millis(300)
+ .max_delay(Duration::from_secs(60 * 60))
+ .map(jitter);
+
+ let should_retry = |err: &jsonrpc_client_core::Error| -> bool {
+ match err.kind() {
+ jsonrpc_client_core::ErrorKind::JsonRpcError(err)
+ if err.code.code() == TOO_MANY_KEYS_ERROR_CODE =>
+ {
+ false
+ }
+ _ => true,
+ }
+ };
+
+ let upload_future =
+ RetryIf::spawn(retry_strategy, future_generator, should_retry).map_err(move |err| {
+ match err {
+ // This should really be unreachable, since the retry strategy is infinite.
+ tokio_retry::Error::OperationError(e) => {
+ log::error!(
+ "{}",
+ e.display_chain_with_msg("Failed to generate wireguard key:")
+ );
+ Self::map_rpc_error(e)
+ }
+ tokio_retry::Error::TimerError(timer_error) => {
+ log::error!("Tokio timer error {}", timer_error);
+ Error::ExectuionError
+ }
+ }
+ });
+
+
+ let (fut, cancel_handle) = Cancellable::new(upload_future);
+ let daemon_tx = self.daemon_tx.clone();
+ let fut = fut.then(move |result| {
+ match result {
+ Ok(wireguard_data) => {
+ let _ = daemon_tx.send(InternalDaemonEvent::WgKeyEvent((
+ account,
+ Ok(wireguard_data),
+ )));
+ }
+ Err(CancelErr::Inner(e)) => {
+ let _ = daemon_tx.send(InternalDaemonEvent::WgKeyEvent((account, Err(e))));
+ }
+ Err(CancelErr::Cancelled) => {
+ log::error!("Key generation cancelled");
+ }
+ };
+ Ok(())
+ });
+
+ match self
+ .tokio_remote
+ .execute(fut)
+ .map_err(|_| Error::ExectuionError)
+ {
+ Ok(res) => {
+ self.current_job = Some(cancel_handle);
+ Ok(res)
+ }
+ Err(e) => Err(e),
+ }
+ }
+
+
+ fn push_future_generator(
+ &self,
+ account: AccountToken,
+ private_key: PrivateKey,
+ ) -> Box<dyn FnMut() -> Box<dyn Future<Item = WireguardData, Error = JsonRpcError> + Send> + Send>
+ {
+ let mut rpc = mullvad_rpc::WireguardKeyProxy::new(self.http_handle.clone());
+ let public_key = private_key.public_key();
+
+ let push_future =
+ move || -> Box<dyn Future<Item = WireguardData, Error = JsonRpcError> + Send> {
+ let key = private_key.clone();
+ Box::new(rpc.push_wg_key(account.clone(), public_key.clone()).map(
+ move |addresses| WireguardData {
+ private_key: key,
+ addresses,
+ },
+ ))
+ };
+ Box::new(push_future)
+ }
+
+ fn map_rpc_error(err: jsonrpc_client_core::Error) -> Error {
+ match err.kind() {
+ // TODO: Consider handling the invalid account case too.
+ jsonrpc_client_core::ErrorKind::JsonRpcError(err) if err.code.code() == -703 => {
+ Error::TooManyKeys
+ }
+ _ => Error::RpcError(err),
+ }
+ }
+}
+
+pub enum CancelErr<E> {
+ Cancelled,
+ Inner(E),
+}
+
+pub struct Cancellable<T, E, F: Future<Item = T, Error = E>> {
+ rx: oneshot::Receiver<()>,
+ f: F,
+}
+
+pub struct CancelHandle {
+ tx: oneshot::Sender<()>,
+}
+
+impl CancelHandle {
+ fn cancel(self) {
+ let _ = self.tx.send(());
+ }
+}
+
+
+impl<T, E, F> Cancellable<T, E, F>
+where
+ F: Future<Item = T, Error = E>,
+{
+ fn new(f: F) -> (Self, CancelHandle) {
+ let (tx, rx) = oneshot::channel();
+ (Self { f, rx }, CancelHandle { tx })
+ }
+}
+
+impl<T, E, F> Future for Cancellable<T, E, F>
+where
+ F: Future<Item = T, Error = E>,
+{
+ type Item = T;
+ type Error = CancelErr<E>;
+
+ fn poll(&mut self) -> Poll<T, CancelErr<E>> {
+ match self.rx.poll() {
+ Ok(Async::Ready(_)) | Err(_) => return Err(CancelErr::Cancelled),
+ Ok(Async::NotReady) => (),
+ };
+
+ match self.f.poll() {
+ Ok(v) => Ok(v),
+ Err(e) => Err(CancelErr::Inner(e)),
+ }
+ }
+}
diff --git a/mullvad-ipc-client/src/lib.rs b/mullvad-ipc-client/src/lib.rs
index 29f8985287..d42f165026 100644
--- a/mullvad-ipc-client/src/lib.rs
+++ b/mullvad-ipc-client/src/lib.rs
@@ -159,7 +159,7 @@ impl DaemonRpcClient {
self.call("get_settings", &NO_ARGS)
}
- pub fn generate_wireguard_key(&mut self) -> Result<()> {
+ pub fn generate_wireguard_key(&mut self) -> Result<mullvad_types::wireguard::KeygenEvent> {
self.call("generate_wireguard_key", &NO_ARGS)
}
diff --git a/mullvad-jni/src/daemon_interface.rs b/mullvad-jni/src/daemon_interface.rs
index e91885fc95..a99c6e7cac 100644
--- a/mullvad-jni/src/daemon_interface.rs
+++ b/mullvad-jni/src/daemon_interface.rs
@@ -2,7 +2,7 @@ use futures::{sync::oneshot, Future};
use mullvad_daemon::{DaemonCommandSender, ManagementCommand};
use mullvad_types::{
account::AccountData, location::GeoIpLocation, relay_constraints::RelaySettingsUpdate,
- relay_list::RelayList, settings::Settings, states::TargetState,
+ relay_list::RelayList, settings::Settings, states::TargetState, wireguard::KeygenEvent,
};
use parking_lot::Mutex;
use talpid_types::{net::wireguard, tunnel::TunnelStateTransition};
@@ -64,14 +64,12 @@ impl DaemonInterface {
Ok(())
}
- pub fn generate_wireguard_key(&self) -> Result<()> {
+ pub fn generate_wireguard_key(&self) -> Result<KeygenEvent> {
let (tx, rx) = oneshot::channel();
self.send_command(ManagementCommand::GenerateWireguardKey(tx))?;
- rx.wait()
- .map_err(|_| Error::NoResponse)?
- .map_err(Error::RpcError)
+ rx.wait().map_err(|_| Error::NoResponse)
}
pub fn get_account_data(&self, account_token: String) -> Result<AccountData> {
diff --git a/mullvad-jni/src/jni_event_listener.rs b/mullvad-jni/src/jni_event_listener.rs
index 9a89d85927..57d9bb6e76 100644
--- a/mullvad-jni/src/jni_event_listener.rs
+++ b/mullvad-jni/src/jni_event_listener.rs
@@ -5,7 +5,7 @@ use jni::{
AttachGuard, JNIEnv,
};
use mullvad_daemon::EventListener;
-use mullvad_types::{relay_list::RelayList, settings::Settings};
+use mullvad_types::{relay_list::RelayList, settings::Settings, wireguard::KeygenEvent};
use std::{sync::mpsc, thread};
use talpid_types::{tunnel::TunnelStateTransition, ErrorExt};
@@ -48,6 +48,9 @@ impl EventListener for JniEventListener {
fn notify_relay_list(&self, relay_list: RelayList) {
let _ = self.0.send(Event::RelayList(relay_list));
}
+
+ // TODO: manage key events properly
+ fn notify_key_event(&self, _key_event: KeygenEvent) {}
}
struct JniEventHandler<'env> {
diff --git a/mullvad-jni/src/lib.rs b/mullvad-jni/src/lib.rs
index e7a6e862fa..947d2b5f70 100644
--- a/mullvad-jni/src/lib.rs
+++ b/mullvad-jni/src/lib.rs
@@ -18,6 +18,7 @@ use jni::{
};
use lazy_static::lazy_static;
use mullvad_daemon::{logging, version, Daemon, DaemonCommandSender};
+use mullvad_types::wireguard::KeygenEvent;
use parking_lot::RwLock;
use std::{
collections::HashMap,
@@ -225,7 +226,12 @@ pub extern "system" fn Java_net_mullvad_mullvadvpn_MullvadDaemon_generateWiregua
_: JObject,
) -> jboolean {
match DAEMON_INTERFACE.generate_wireguard_key() {
- Ok(()) => JNI_TRUE,
+ Ok(KeygenEvent::NewKey(_)) => JNI_TRUE,
+ // TODO: Handle the new result better.
+ Ok(keygen_failure) => {
+ log::error!("Failed to generate wireguard key {:?}", keygen_failure);
+ JNI_FALSE
+ }
Err(error) => {
log::error!(
"{}",
diff --git a/mullvad-types/src/lib.rs b/mullvad-types/src/lib.rs
index 6947c4ee9c..75088341d3 100644
--- a/mullvad-types/src/lib.rs
+++ b/mullvad-types/src/lib.rs
@@ -34,4 +34,7 @@ pub enum DaemonEvent {
/// The daemon got an updated relay list.
RelayList(relay_list::RelayList),
+
+ /// Key event
+ WireguardKey(wireguard::KeygenEvent),
}
diff --git a/mullvad-types/src/wireguard.rs b/mullvad-types/src/wireguard.rs
index 5fc0846557..635bb7d7da 100644
--- a/mullvad-types/src/wireguard.rs
+++ b/mullvad-types/src/wireguard.rs
@@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize};
+use std::fmt;
use talpid_types::net::wireguard;
/// Contains account specific wireguard data
@@ -15,3 +16,22 @@ pub struct AssociatedAddresses {
pub ipv4_address: ipnetwork::Ipv4Network,
pub ipv6_address: ipnetwork::Ipv6Network,
}
+
+#[serde(rename_all = "snake_case")]
+#[derive(Clone, Debug, Deserialize, Serialize)]
+/// Event that is emitted when the daemon has finished generating a key.
+pub enum KeygenEvent {
+ NewKey(wireguard::PublicKey),
+ TooManyKeys,
+ GenerationFailure,
+}
+
+impl fmt::Display for KeygenEvent {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ match self {
+ KeygenEvent::NewKey(public_key) => write!(f, "New wireguard key {}", public_key),
+ KeygenEvent::TooManyKeys => write!(f, "Account has too many keys already"),
+ KeygenEvent::GenerationFailure => write!(f, "Failed to generate new wireguard key"),
+ }
+ }
+}