diff --git a/mixnode/src/commands/init.rs b/mixnode/src/commands/init.rs index 09421e8c170..e5e2936cb01 100644 --- a/mixnode/src/commands/init.rs +++ b/mixnode/src/commands/init.rs @@ -16,7 +16,7 @@ use crate::commands::override_config; use crate::config::persistence::pathfinder::MixNodePathfinder; use clap::{App, Arg, ArgMatches}; use config::NymConfig; -use crypto::asymmetric::encryption; +use crypto::asymmetric::{encryption, identity}; use directory_client::DirectoryClient; use log::*; use nymsphinx::params::DEFAULT_NUM_MIX_HOPS; @@ -142,8 +142,18 @@ pub fn execute(matches: &ArgMatches) { config = config.with_layer(layer); debug!("Choosing layer {}", config.get_layer()); + let identity_keys = identity::KeyPair::new(); let sphinx_keys = encryption::KeyPair::new(); let pathfinder = MixNodePathfinder::new_from_config(&config); + pemstore::store_keypair( + &identity_keys, + &pemstore::KeyPairPath::new( + pathfinder.private_identity_key().to_owned(), + pathfinder.public_identity_key().to_owned(), + ), + ) + .expect("Failed to save identity keys"); + pemstore::store_keypair( &sphinx_keys, &pemstore::KeyPairPath::new( @@ -152,7 +162,8 @@ pub fn execute(matches: &ArgMatches) { ), ) .expect("Failed to save sphinx keys"); - println!("Saved mixnet sphinx keypair"); + + println!("Saved mixnet identity and sphinx keypairs"); let config_save_location = config.get_config_file_save_location(); config .save_to_file(None) diff --git a/mixnode/src/commands/run.rs b/mixnode/src/commands/run.rs index 54e58b45d9c..c6b5c4d5666 100644 --- a/mixnode/src/commands/run.rs +++ b/mixnode/src/commands/run.rs @@ -17,7 +17,7 @@ use crate::config::{persistence::pathfinder::MixNodePathfinder, Config}; use crate::node::MixNode; use clap::{App, Arg, ArgMatches}; use config::NymConfig; -use crypto::asymmetric::encryption; +use crypto::asymmetric::{encryption, identity}; pub fn command_args<'a, 'b>() -> App<'a, 'b> { App::new("run") @@ -95,6 +95,19 @@ fn special_addresses() -> Vec<&'static str> { vec!["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"] } +fn load_identity_keys(pathfinder: &MixNodePathfinder) -> identity::KeyPair { + let identity_keypair: identity::KeyPair = pemstore::load_keypair(&pemstore::KeyPairPath::new( + pathfinder.private_identity_key().to_owned(), + pathfinder.public_identity_key().to_owned(), + )) + .expect("Failed to read stored identity key files"); + println!( + "Public identity key: {}\n", + identity_keypair.public_key().to_base58_string() + ); + identity_keypair +} + fn load_sphinx_keys(pathfinder: &MixNodePathfinder) -> encryption::KeyPair { let sphinx_keypair: encryption::KeyPair = pemstore::load_keypair(&pemstore::KeyPairPath::new( pathfinder.private_encryption_key().to_owned(), @@ -120,6 +133,7 @@ pub fn execute(matches: &ArgMatches) { config = override_config(config, matches); let pathfinder = MixNodePathfinder::new_from_config(&config); + let identity_keypair = load_identity_keys(&pathfinder); let sphinx_keypair = load_sphinx_keys(&pathfinder); let listening_ip_string = config.get_listening_address().ip().to_string(); @@ -145,5 +159,5 @@ pub fn execute(matches: &ArgMatches) { config.get_announce_address() ); - MixNode::new(config, sphinx_keypair).run(); + MixNode::new(config, identity_keypair, sphinx_keypair).run(); } diff --git a/mixnode/src/commands/upgrade.rs b/mixnode/src/commands/upgrade.rs index b074d2f1baa..511640a3e1e 100644 --- a/mixnode/src/commands/upgrade.rs +++ b/mixnode/src/commands/upgrade.rs @@ -12,10 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::config::{Config, MISSING_VALUE}; +use crate::config::{missing_string_value, Config}; use clap::{App, Arg, ArgMatches}; use config::NymConfig; +use crypto::asymmetric::identity; use std::fmt::Display; +use std::path::PathBuf; use std::process; use version_checker::{parse_version, Version}; @@ -41,6 +43,22 @@ fn print_successful_upgrade(from: D1, to: D2) { } fn pre_090_upgrade(from: &str, config: Config) -> Config { + // note: current is guaranteed to not have any `build` information suffix (nor pre-release + // information), as this was asserted at the beginning of this command) + // + // upgrade to current (if it's a 0.9.X) or try to upgrade to 0.9.0 as an intermediate + // step in future upgrades (so, for example, we might go 0.8.0 -> 0.9.0 -> 0.10.0) + // this way we don't need to have all the crazy paths on how to upgrade from any version to any + // other version. We just upgrade one minor version at a time. + let current = Version::parse(env!("CARGO_PKG_VERSION")).unwrap(); + let to_version = if current.major == 0 && current.minor == 9 { + current + } else { + Version::new(0, 9, 0) + }; + + print_start_upgrade(&from, &to_version); + // this is not extracted to separate function as you only have to manually pass version // if upgrading from pre090 version let from = match from.strip_prefix("v") { @@ -57,31 +75,41 @@ fn pre_090_upgrade(from: &str, config: Config) -> Config { if from_version.major == 0 && from_version.minor < 8 { // technically this could be implemented, but is there any point in that? eprintln!("upgrading node from before v0.8.0 is not supported. Please run `init` with new binary instead"); + print_failed_upgrade(&from_version, &to_version); process::exit(1) } if (from_version.major == 0 && from_version.minor >= 9) || from_version.major >= 1 { eprintln!("self reported version is higher than 0.9.0. Those releases should have already contained version numbers in config! Make sure you provided correct version"); + print_failed_upgrade(&from_version, &to_version); process::exit(1) } - // note: current is guaranteed to not have any `build` information suffix (nor pre-release - // information), as this was asserted at the beginning of this command) - // - // upgrade to current (if it's a 0.9.X) or try to upgrade to 0.9.0 as an intermediate - // step in future upgrades (so, for example, we might go 0.8.0 -> 0.9.0 -> 0.10.0) - // this way we don't need to have all the crazy paths on how to upgrade from any version to any - // other version. We just upgrade one minor version at a time. - let current = Version::parse(env!("CARGO_PKG_VERSION")).unwrap(); - let to_version = if current.major == 0 && current.minor == 9 { - current - } else { - Version::new(0, 9, 0) - }; + if config.get_private_identity_key_file() != missing_string_value::() + || config.get_public_identity_key_file() != missing_string_value::() + { + eprintln!("existing config seems to have specified identity keys which were only introduced in 0.9.0! Can't perform upgrade."); + print_failed_upgrade(&from_version, &to_version); + process::exit(1); + } - print_start_upgrade(&from_version, &to_version); + let mut upgraded_config = config.with_custom_version(to_version.to_string().as_ref()); + + println!("Generating new identity..."); + let identity_keys = identity::KeyPair::new(); + upgraded_config.set_default_identity_keypair_paths(); + + if let Err(err) = pemstore::store_keypair( + &identity_keys, + &pemstore::KeyPairPath::new( + upgraded_config.get_private_identity_key_file(), + upgraded_config.get_public_identity_key_file(), + ), + ) { + eprintln!("Failed to save new identity key files! - {}", err); + process::exit(1); + } - let upgraded_config = config.with_custom_version(to_version.to_string().as_ref()); // TODO: THIS IS INCOMPLETE AS ONCE PRESENCE IS REMOVED IN 0.9.0 IT WILL ALSO NEED // TO BE PURGED FROM CONFIG @@ -135,7 +163,7 @@ pub fn execute(matches: &ArgMatches) { }); // versions fields were added in 0.9.0 - if existing_config.get_version() == MISSING_VALUE { + if existing_config.get_version() == missing_string_value::() { let self_reported_version = matches.value_of("current version").unwrap_or_else(|| { eprintln!( "trying to upgrade from pre v0.9.0 without providing current system version!" diff --git a/mixnode/src/config/mod.rs b/mixnode/src/config/mod.rs index 925d9b4c01f..6239fcdadf8 100644 --- a/mixnode/src/config/mod.rs +++ b/mixnode/src/config/mod.rs @@ -86,8 +86,8 @@ impl NymConfig for Config { } } -pub fn missing_string_value() -> String { - MISSING_VALUE.to_string() +pub fn missing_string_value>() -> T { + MISSING_VALUE.to_string().into() } impl Config { @@ -98,6 +98,20 @@ impl Config { // builder methods pub fn with_id>(mut self, id: S) -> Self { let id = id.into(); + if self + .mixnode + .private_identity_key_file + .as_os_str() + .is_empty() + { + self.mixnode.private_identity_key_file = + self::MixNode::default_private_identity_key_file(&id); + } + if self.mixnode.public_identity_key_file.as_os_str().is_empty() { + self.mixnode.public_identity_key_file = + self::MixNode::default_public_identity_key_file(&id); + } + if self.mixnode.private_sphinx_key_file.as_os_str().is_empty() { self.mixnode.private_sphinx_key_file = self::MixNode::default_private_sphinx_key_file(&id); @@ -106,6 +120,7 @@ impl Config { self.mixnode.public_sphinx_key_file = self::MixNode::default_public_sphinx_key_file(&id); } + self.mixnode.id = id; self } @@ -218,6 +233,14 @@ impl Config { self.mixnode.location.clone() } + pub fn get_private_identity_key_file(&self) -> PathBuf { + self.mixnode.private_identity_key_file.clone() + } + + pub fn get_public_identity_key_file(&self) -> PathBuf { + self.mixnode.public_identity_key_file.clone() + } + pub fn get_private_sphinx_key_file(&self) -> PathBuf { self.mixnode.private_sphinx_key_file.clone() } @@ -277,6 +300,14 @@ impl Config { pub fn get_version(&self) -> &str { &self.mixnode.version } + + // upgrade-specific + pub(crate) fn set_default_identity_keypair_paths(&mut self) { + self.mixnode.private_identity_key_file = + self::MixNode::default_private_identity_key_file(&self.mixnode.id); + self.mixnode.public_identity_key_file = + self::MixNode::default_public_identity_key_file(&self.mixnode.id); + } } #[derive(Debug, Deserialize, PartialEq, Serialize)] @@ -309,6 +340,14 @@ pub struct MixNode { /// `listening_address`. announce_address: String, + /// Path to file containing private identity key. + #[serde(default = "missing_string_value")] + private_identity_key_file: PathBuf, + + /// Path to file containing public identity key. + #[serde(default = "missing_string_value")] + public_identity_key_file: PathBuf, + /// Path to file containing private sphinx key. private_sphinx_key_file: PathBuf, @@ -329,6 +368,14 @@ pub struct MixNode { } impl MixNode { + fn default_private_identity_key_file(id: &str) -> PathBuf { + Config::default_data_directory(Some(id)).join("private_identity.pem") + } + + fn default_public_identity_key_file(id: &str) -> PathBuf { + Config::default_data_directory(Some(id)).join("public_identity.pem") + } + fn default_private_sphinx_key_file(id: &str) -> PathBuf { Config::default_data_directory(Some(id)).join("private_sphinx.pem") } @@ -353,6 +400,8 @@ impl Default for MixNode { .parse() .unwrap(), announce_address: format!("127.0.0.1:{}", DEFAULT_LISTENING_PORT), + private_identity_key_file: Default::default(), + public_identity_key_file: Default::default(), private_sphinx_key_file: Default::default(), public_sphinx_key_file: Default::default(), presence_directory_server: DEFAULT_DIRECTORY_SERVER.to_string(), diff --git a/mixnode/src/config/persistence/pathfinder.rs b/mixnode/src/config/persistence/pathfinder.rs index 3d944c952f3..ba9214b7a88 100644 --- a/mixnode/src/config/persistence/pathfinder.rs +++ b/mixnode/src/config/persistence/pathfinder.rs @@ -17,7 +17,8 @@ use std::path::{Path, PathBuf}; #[derive(Debug)] pub struct MixNodePathfinder { - config_dir: PathBuf, + identity_private_key: PathBuf, + identity_public_key: PathBuf, private_sphinx_key: PathBuf, public_sphinx_key: PathBuf, } @@ -25,12 +26,21 @@ pub struct MixNodePathfinder { impl MixNodePathfinder { pub fn new_from_config(config: &Config) -> Self { MixNodePathfinder { - config_dir: config.get_config_file_save_location(), + identity_private_key: config.get_private_identity_key_file(), + identity_public_key: config.get_public_identity_key_file(), private_sphinx_key: config.get_private_sphinx_key_file(), public_sphinx_key: config.get_public_sphinx_key_file(), } } + pub fn private_identity_key(&self) -> &Path { + &self.identity_private_key + } + + pub fn public_identity_key(&self) -> &Path { + &self.identity_public_key + } + pub fn private_encryption_key(&self) -> &Path { &self.private_sphinx_key } diff --git a/mixnode/src/config/template.rs b/mixnode/src/config/template.rs index ff0f1a1728f..f2425a53734 100644 --- a/mixnode/src/config/template.rs +++ b/mixnode/src/config/template.rs @@ -42,6 +42,12 @@ layer = {{ mixnode.layer }} # Socket address to which this mixnode will bind to and will be listening for packets. listening_address = '{{ mixnode.listening_address }}' +# Path to file containing private identity key. +private_identity_key_file = '{{ mixnode.private_identity_key_file }}' + +# Path to file containing public identity key. +public_identity_key_file = '{{ mixnode.public_identity_key_file }}' + # Path to file containing private identity key. private_sphinx_key_file = '{{ mixnode.private_sphinx_key_file }}' diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index f6c6043af82..8f1fb7ccac3 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -16,7 +16,7 @@ use crate::config::Config; use crate::node::listener::connection_handler::packet_processing::PacketProcessor; use crate::node::listener::connection_handler::ConnectionHandler; use crate::node::listener::Listener; -use crypto::asymmetric::encryption; +use crypto::asymmetric::{encryption, identity}; use directory_client::DirectoryClient; use log::*; use mixnet_client::forwarder::{MixForwardingSender, PacketForwarder}; @@ -30,13 +30,20 @@ mod presence; // the MixNode will live for whole duration of this program pub struct MixNode { config: Config, + #[allow(dead_code)] + identity_keypair: Arc, sphinx_keypair: Arc, } impl MixNode { - pub fn new(config: Config, sphinx_keypair: encryption::KeyPair) -> Self { + pub fn new( + config: Config, + identity_keypair: identity::KeyPair, + sphinx_keypair: encryption::KeyPair, + ) -> Self { MixNode { config, + identity_keypair: Arc::new(identity_keypair), sphinx_keypair: Arc::new(sphinx_keypair), } }