// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of OpenEthereum. // OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with OpenEthereum. If not, see . extern crate dir; extern crate docopt; extern crate ethstore; extern crate num_cpus; extern crate panic_hook; extern crate parking_lot; extern crate rustc_hex; extern crate serde; extern crate env_logger; #[macro_use] extern crate serde_derive; use std::{collections::VecDeque, env, fmt, fs, io::Read, process}; use docopt::Docopt; use ethstore::{ accounts_dir::{KeyDirectory, RootDiskDirectory}, ethkey::{Address, Password}, import_accounts, EthStore, PresaleWallet, SecretStore, SecretVaultRef, SimpleSecretStore, StoreAccountRef, }; mod crack; pub const USAGE: &'static str = r#" Parity Ethereum key management tool. Copyright 2015-2019 Parity Technologies (UK) Ltd. Usage: ethstore insert [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD] ethstore change-pwd
[--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD] ethstore list [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD] ethstore import [] [--src DIR] [--dir DIR] ethstore import-wallet [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD] ethstore find-wallet-pass ethstore remove
[--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD] ethstore sign
[--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD] ethstore public
[--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD] ethstore list-vaults [--dir DIR] ethstore create-vault [--dir DIR] ethstore change-vault-pwd [--dir DIR] ethstore move-to-vault
[--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD] ethstore move-from-vault
[--dir DIR] ethstore [-h | --help] Options: -h, --help Display this message and exit. --dir DIR Specify the secret store directory. It may be either parity, parity-(chain), geth, geth-test or a path [default: parity]. --vault VAULT Specify vault to use in this operation. --vault-pwd VAULTPWD Specify vault password to use in this operation. Please note that this option is required when vault option is set. Otherwise it is ignored. --src DIR Specify import source. It may be either parity, parity-(chain), geth, geth-test or a path [default: geth]. Commands: insert Save account with password. change-pwd Change password. list List accounts. import Import accounts from src. import-wallet Import presale wallet. find-wallet-pass Tries to open a wallet with list of passwords given. remove Remove account. sign Sign message. public Displays public key for an address. list-vaults List vaults. create-vault Create new vault. change-vault-pwd Change vault password. move-to-vault Move account to vault from another vault/root directory. move-from-vault Move account to root directory from given vault. "#; #[derive(Debug, Deserialize)] struct Args { cmd_insert: bool, cmd_change_pwd: bool, cmd_list: bool, cmd_import: bool, cmd_import_wallet: bool, cmd_find_wallet_pass: bool, cmd_remove: bool, cmd_sign: bool, cmd_public: bool, cmd_list_vaults: bool, cmd_create_vault: bool, cmd_change_vault_pwd: bool, cmd_move_to_vault: bool, cmd_move_from_vault: bool, arg_secret: String, arg_password: String, arg_old_pwd: String, arg_new_pwd: String, arg_address: String, arg_message: String, arg_path: String, arg_vault: String, flag_src: String, flag_dir: String, flag_vault: String, flag_vault_pwd: String, } enum Error { Ethstore(ethstore::Error), Docopt(docopt::Error), } impl From for Error { fn from(err: ethstore::Error) -> Self { Error::Ethstore(err) } } impl From for Error { fn from(err: docopt::Error) -> Self { Error::Docopt(err) } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Error::Ethstore(ref err) => fmt::Display::fmt(err, f), Error::Docopt(ref err) => fmt::Display::fmt(err, f), } } } fn main() { panic_hook::set_abort(); if env::var("RUST_LOG").is_err() { env::set_var("RUST_LOG", "warn") } env_logger::try_init().expect("Logger initialized only once."); match execute(env::args()) { Ok(result) => println!("{}", result), Err(Error::Docopt(ref e)) => e.exit(), Err(err) => { eprintln!("{}", err); process::exit(1); } } } fn key_dir(location: &str, password: Option) -> Result, Error> { let dir: RootDiskDirectory = match location { path if path.starts_with("parity") => { let chain = path.split('-').nth(1).unwrap_or("ethereum"); let mut path = dir::default_data_pathbuf(); path.push("keys"); path.push(chain); RootDiskDirectory::create(path)? } path => RootDiskDirectory::create(path)?, }; Ok(Box::new(dir.with_password(password))) } fn open_args_vault(store: &EthStore, args: &Args) -> Result { if args.flag_vault.is_empty() { return Ok(SecretVaultRef::Root); } let vault_pwd = load_password(&args.flag_vault_pwd)?; store.open_vault(&args.flag_vault, &vault_pwd)?; Ok(SecretVaultRef::Vault(args.flag_vault.clone())) } fn open_args_vault_account( store: &EthStore, address: Address, args: &Args, ) -> Result { match open_args_vault(store, args)? { SecretVaultRef::Root => Ok(StoreAccountRef::root(address)), SecretVaultRef::Vault(name) => Ok(StoreAccountRef::vault(&name, address)), } } fn format_accounts(accounts: &[Address]) -> String { accounts .iter() .enumerate() .map(|(i, a)| format!("{:2}: 0x{:x}", i, a)) .collect::>() .join("\n") } fn format_vaults(vaults: &[String]) -> String { vaults.join("\n") } fn load_password(path: &str) -> Result { let mut file = fs::File::open(path).map_err(|e| { ethstore::Error::Custom(format!("Error opening password file '{}': {}", path, e)) })?; let mut password = String::new(); file.read_to_string(&mut password).map_err(|e| { ethstore::Error::Custom(format!("Error reading password file '{}': {}", path, e)) })?; // drop EOF let _ = password.pop(); Ok(password.into()) } fn execute(command: I) -> Result where I: IntoIterator, S: AsRef, { let args: Args = Docopt::new(USAGE).and_then(|d| d.argv(command).deserialize())?; let store = EthStore::open(key_dir(&args.flag_dir, None)?)?; return if args.cmd_insert { let secret = args .arg_secret .parse() .map_err(|_| ethstore::Error::InvalidSecret)?; let password = load_password(&args.arg_password)?; let vault_ref = open_args_vault(&store, &args)?; let account_ref = store.insert_account(vault_ref, secret, &password)?; Ok(format!("0x{:x}", account_ref.address)) } else if args.cmd_change_pwd { let address = args .arg_address .parse() .map_err(|_| ethstore::Error::InvalidAccount)?; let old_pwd = load_password(&args.arg_old_pwd)?; let new_pwd = load_password(&args.arg_new_pwd)?; let account_ref = open_args_vault_account(&store, address, &args)?; let ok = store .change_password(&account_ref, &old_pwd, &new_pwd) .is_ok(); Ok(format!("{}", ok)) } else if args.cmd_list { let vault_ref = open_args_vault(&store, &args)?; let accounts = store.accounts()?; let accounts: Vec<_> = accounts .into_iter() .filter(|a| &a.vault == &vault_ref) .map(|a| a.address) .collect(); Ok(format_accounts(&accounts)) } else if args.cmd_import { let password = match args.arg_password.as_ref() { "" => None, _ => Some(load_password(&args.arg_password)?), }; let src = key_dir(&args.flag_src, password)?; let dst = key_dir(&args.flag_dir, None)?; let accounts = import_accounts(&*src, &*dst)?; Ok(format_accounts(&accounts)) } else if args.cmd_import_wallet { let wallet = PresaleWallet::open(&args.arg_path)?; let password = load_password(&args.arg_password)?; let kp = wallet.decrypt(&password)?; let vault_ref = open_args_vault(&store, &args)?; let account_ref = store.insert_account(vault_ref, kp.secret().clone(), &password)?; Ok(format!("0x{:x}", account_ref.address)) } else if args.cmd_find_wallet_pass { let passwords = load_password(&args.arg_password)?; let passwords = passwords .as_str() .lines() .map(|line| str::to_owned(line).into()) .collect::>(); crack::run(passwords, &args.arg_path)?; Ok(format!("Password not found.")) } else if args.cmd_remove { let address = args .arg_address .parse() .map_err(|_| ethstore::Error::InvalidAccount)?; let password = load_password(&args.arg_password)?; let account_ref = open_args_vault_account(&store, address, &args)?; let ok = store.remove_account(&account_ref, &password).is_ok(); Ok(format!("{}", ok)) } else if args.cmd_sign { let address = args .arg_address .parse() .map_err(|_| ethstore::Error::InvalidAccount)?; let message = args .arg_message .parse() .map_err(|_| ethstore::Error::InvalidMessage)?; let password = load_password(&args.arg_password)?; let account_ref = open_args_vault_account(&store, address, &args)?; let signature = store.sign(&account_ref, &password, &message)?; Ok(format!("0x{}", signature)) } else if args.cmd_public { let address = args .arg_address .parse() .map_err(|_| ethstore::Error::InvalidAccount)?; let password = load_password(&args.arg_password)?; let account_ref = open_args_vault_account(&store, address, &args)?; let public = store.public(&account_ref, &password)?; Ok(format!("0x{:x}", public)) } else if args.cmd_list_vaults { let vaults = store.list_vaults()?; Ok(format_vaults(&vaults)) } else if args.cmd_create_vault { let password = load_password(&args.arg_password)?; store.create_vault(&args.arg_vault, &password)?; Ok("OK".to_owned()) } else if args.cmd_change_vault_pwd { let old_pwd = load_password(&args.arg_old_pwd)?; let new_pwd = load_password(&args.arg_new_pwd)?; store.open_vault(&args.arg_vault, &old_pwd)?; store.change_vault_password(&args.arg_vault, &new_pwd)?; Ok("OK".to_owned()) } else if args.cmd_move_to_vault { let address = args .arg_address .parse() .map_err(|_| ethstore::Error::InvalidAccount)?; let password = load_password(&args.arg_password)?; let account_ref = open_args_vault_account(&store, address, &args)?; store.open_vault(&args.arg_vault, &password)?; store.change_account_vault(SecretVaultRef::Vault(args.arg_vault), account_ref)?; Ok("OK".to_owned()) } else if args.cmd_move_from_vault { let address = args .arg_address .parse() .map_err(|_| ethstore::Error::InvalidAccount)?; let password = load_password(&args.arg_password)?; store.open_vault(&args.arg_vault, &password)?; store.change_account_vault( SecretVaultRef::Root, StoreAccountRef::vault(&args.arg_vault, address), )?; Ok("OK".to_owned()) } else { Ok(format!("{}", USAGE)) }; }