diff --git a/ethstore/README.md b/ethstore/README.md index aba4911bf7d..0b85d99b431 100644 --- a/ethstore/README.md +++ b/ethstore/README.md @@ -20,6 +20,7 @@ Usage: ethstore change-pwd
[--dir DIR] ethstore list [--dir DIR] ethstore import [--src DIR] [--dir DIR] + ethstore import-wallet [--dir DIR] ethstore remove
[--dir DIR] ethstore sign
[--dir DIR] ethstore [-h | --help] @@ -38,6 +39,7 @@ Commands: change-pwd Change account password. list List accounts. import Import accounts from src. + import-wallet Import presale wallet. remove Remove account. sign Sign message. ``` @@ -48,11 +50,11 @@ Commands: *Encrypt secret with a password and save it in secret store.* - `` - ethereum secret, 32 bytes long -- `` - account password, any string +- `` - account password, file path - `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity ``` -ethstore insert 7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5 "this is sparta" +ethstore insert 7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5 password.txt ``` ``` @@ -75,12 +77,12 @@ ethstore insert `ethkey generate random -s` "this is sparta" *Change account password.* - `
` - ethereum address, 20 bytes long -- `` - old account password, any string -- `` - new account password, any string +- `` - old account password, file path +- `` - new account password, file path - `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity ``` -ethstore change-pwd a8fa5dd30a87bb9e3288d604eb74949c515ab66e "this is sparta" "hello world" +ethstore change-pwd a8fa5dd30a87bb9e3288d604eb74949c515ab66e old_pwd.txt new_pwd.txt ``` ``` @@ -112,6 +114,10 @@ ethstore list - `[--src DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: geth - `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity +``` +ethstore import +``` + ``` 0: e6a3d25a7cb7cd21cb720df5b5e8afd154af1bbb 1: 6edddfc6349aff20bc6467ccf276c5b52487f7a8 @@ -119,15 +125,32 @@ ethstore list -- +#### `import-wallet [--dir DIR]` +*Import account from presale wallet.* + +- `` - presale wallet path +- `` - account password, file path +- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity + +``` +ethstore import-wallet ethwallet.json password.txt +``` + +``` +e6a3d25a7cb7cd21cb720df5b5e8afd154af1bbb +``` + +-- + #### `remove
[--dir DIR]` *Remove account from secret store.* - `
` - ethereum address, 20 bytes long -- `` - account password, any string +- `` - account password, file path - `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity ``` -ethstore remove a8fa5dd30a87bb9e3288d604eb74949c515ab66e "hello world" +ethstore remove a8fa5dd30a87bb9e3288d604eb74949c515ab66e password.txt ``` ``` @@ -140,12 +163,12 @@ true *Sign message with account's secret.* - `
` - ethereum address, 20 bytes long -- `` - account password, any string +- `` - account password, file path - `` - message to sign, 32 bytes long - `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity ``` -ethstore sign 24edfff680d536a5f6fe862d36df6f8f6f40f115 "this is sparta" 7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5 +ethstore sign 24edfff680d536a5f6fe862d36df6f8f6f40f115 password.txt 7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5 ``` ``` diff --git a/ethstore/src/bin/ethstore.rs b/ethstore/src/bin/ethstore.rs index 6020679d4e4..5683a8116b3 100644 --- a/ethstore/src/bin/ethstore.rs +++ b/ethstore/src/bin/ethstore.rs @@ -18,13 +18,14 @@ extern crate rustc_serialize; extern crate docopt; extern crate ethstore; -use std::{env, process}; +use std::{env, process, fs}; +use std::io::Read; use std::ops::Deref; use std::str::FromStr; use docopt::Docopt; use ethstore::ethkey::{Secret, Address, Message}; use ethstore::dir::{KeyDirectory, ParityDirectory, DiskDirectory, GethDirectory, DirectoryType}; -use ethstore::{EthStore, SecretStore, import_accounts, Error}; +use ethstore::{EthStore, SecretStore, import_accounts, Error, PresaleWallet}; pub const USAGE: &'static str = r#" Ethereum key management. @@ -35,6 +36,7 @@ Usage: ethstore change-pwd
[--dir DIR] ethstore list [--dir DIR] ethstore import [--src DIR] [--dir DIR] + ethstore import-wallet [--dir DIR] ethstore remove
[--dir DIR] ethstore sign
[--dir DIR] ethstore [-h | --help] @@ -53,6 +55,7 @@ Commands: change-pwd Change password. list List accounts. import Import accounts from src. + import-wallet Import presale wallet. remove Remove account. sign Sign message. "#; @@ -63,6 +66,7 @@ struct Args { cmd_change_pwd: bool, cmd_list: bool, cmd_import: bool, + cmd_import_wallet: bool, cmd_remove: bool, cmd_sign: bool, arg_secret: String, @@ -71,6 +75,7 @@ struct Args { arg_new_pwd: String, arg_address: String, arg_message: String, + arg_path: String, flag_src: String, flag_dir: String, } @@ -105,6 +110,15 @@ fn format_accounts(accounts: &[Address]) -> String { .join("\n") } +fn load_password(path: &str) -> Result { + let mut file = try!(fs::File::open(path)); + let mut password = String::new(); + try!(file.read_to_string(&mut password)); + // drop EOF + let _ = password.pop(); + Ok(password) +} + fn execute(command: I) -> Result where I: IntoIterator, S: AsRef { let args: Args = Docopt::new(USAGE) .and_then(|d| d.argv(command).decode()) @@ -114,11 +128,14 @@ fn execute(command: I) -> Result where I: IntoIterator(command: I) -> Result where I: IntoIterator for [u8] { pub mod aes { use rcrypto::blockmodes::{CtrMode, CbcDecryptor, PkcsPadding}; use rcrypto::aessafe::{AesSafe128Encryptor, AesSafe128Decryptor}; - use rcrypto::symmetriccipher::{Encryptor, Decryptor}; + use rcrypto::symmetriccipher::{Encryptor, Decryptor, SymmetricCipherError}; use rcrypto::buffer::{RefReadBuffer, RefWriteBuffer}; /// Encrypt a message @@ -83,9 +83,10 @@ pub mod aes { } /// Decrypt a message using cbc mode - pub fn decrypt_cbc(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) { + pub fn decrypt_cbc(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) -> Result<(), SymmetricCipherError> { let mut encryptor = CbcDecryptor::new(AesSafe128Decryptor::new(k), PkcsPadding, iv.to_vec()); - encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true).expect("Invalid length or padding"); + try!(encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true)); + Ok(()) } } diff --git a/ethstore/src/presale.rs b/ethstore/src/presale.rs index 5ba57b8d4e1..09c86abeae7 100644 --- a/ethstore/src/presale.rs +++ b/ethstore/src/presale.rs @@ -43,7 +43,7 @@ impl PresaleWallet { pbkdf2(&mut h_mac, password.as_bytes(), 2000, &mut derived_key); let mut key = [0u8; 64]; - crypto::aes::decrypt_cbc(&derived_key, &self.iv, &self.ciphertext, &mut key); + try!(crypto::aes::decrypt_cbc(&derived_key, &self.iv, &self.ciphertext, &mut key).map_err(|_| Error::InvalidPassword)); let secret = Secret::from(key.keccak256()); if let Ok(kp) = KeyPair::from_secret(secret) { @@ -58,7 +58,6 @@ impl PresaleWallet { #[cfg(test)] mod tests { - use ethkey::Address; use super::PresaleWallet; use json; @@ -74,7 +73,7 @@ mod tests { let wallet = json::PresaleWallet::load(json.as_bytes()).unwrap(); let wallet = PresaleWallet::from(wallet); - let kp = wallet.decrypt("123").unwrap(); - assert_eq!(kp.address(), Address::from(wallet.address)); + assert!(wallet.decrypt("123").is_ok()); + assert!(wallet.decrypt("124").is_err()); } } diff --git a/parity/cli.rs b/parity/cli.rs index 1e77031e283..144e2e51cb8 100644 --- a/parity/cli.rs +++ b/parity/cli.rs @@ -25,6 +25,7 @@ Usage: parity daemon [options] parity account (new | list ) [options] parity account import ... [options] + parity wallet import --password FILE [options] parity import [ ] [options] parity export [ ] [options] parity signer new-token [options] @@ -223,6 +224,7 @@ Miscellaneous Options: pub struct Args { pub cmd_daemon: bool, pub cmd_account: bool, + pub cmd_wallet: bool, pub cmd_new: bool, pub cmd_list: bool, pub cmd_export: bool, diff --git a/parity/main.rs b/parity/main.rs index a44cdf6d30d..b610e3025c5 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -129,6 +129,11 @@ fn execute(conf: Configuration) { return; } + if conf.args.cmd_wallet { + execute_wallet_cli(conf); + return; + } + if conf.args.cmd_export { execute_export(conf); return; @@ -534,6 +539,30 @@ fn execute_account_cli(conf: Configuration) { } } +fn execute_wallet_cli(conf: Configuration) { + use ethcore::ethstore::{PresaleWallet, SecretStore, EthStore}; + use ethcore::ethstore::dir::DiskDirectory; + use ethcore::account_provider::AccountProvider; + + let wallet_path = conf.args.arg_path.first().unwrap(); + let filename = conf.args.flag_password.first().unwrap(); + let mut file = File::open(filename).unwrap_or_else(|_| die!("{} Unable to read password file.", filename)); + let mut file_content = String::new(); + file.read_to_string(&mut file_content).unwrap_or_else(|_| die!("{} Unable to read password file.", filename)); + + let dir = Box::new(DiskDirectory::create(conf.keys_path()).unwrap()); + let iterations = conf.keys_iterations(); + let store = AccountProvider::new(Box::new(EthStore::open_with_iterations(dir, iterations).unwrap())); + + // remove eof + let pass = &file_content[..file_content.len() - 1]; + let wallet = PresaleWallet::open(wallet_path).unwrap_or_else(|_| die!("Unable to open presale wallet.")); + let kp = wallet.decrypt(pass).unwrap_or_else(|_| die!("Invalid password")); + let address = store.insert_account(kp.secret().clone(), pass).unwrap(); + + println!("Imported account: {}", address); +} + fn wait_for_exit( panic_handler: Arc, _rpc_server: Option,