Merge pull request #1376 from ethcore/presale_wallet

Presale wallet
This commit is contained in:
Arkadiy Paronyan 2016-06-22 20:14:35 +02:00 committed by GitHub
commit cb9b1e2ad5
6 changed files with 101 additions and 22 deletions

View File

@ -20,6 +20,7 @@ Usage:
ethstore change-pwd <address> <old-pwd> <new-pwd> [--dir DIR] ethstore change-pwd <address> <old-pwd> <new-pwd> [--dir DIR]
ethstore list [--dir DIR] ethstore list [--dir DIR]
ethstore import [--src DIR] [--dir DIR] ethstore import [--src DIR] [--dir DIR]
ethstore import-wallet <path> <password> [--dir DIR]
ethstore remove <address> <password> [--dir DIR] ethstore remove <address> <password> [--dir DIR]
ethstore sign <address> <password> <message> [--dir DIR] ethstore sign <address> <password> <message> [--dir DIR]
ethstore [-h | --help] ethstore [-h | --help]
@ -38,6 +39,7 @@ Commands:
change-pwd Change account password. change-pwd Change account password.
list List accounts. list List accounts.
import Import accounts from src. import Import accounts from src.
import-wallet Import presale wallet.
remove Remove account. remove Remove account.
sign Sign message. sign Sign message.
``` ```
@ -48,11 +50,11 @@ Commands:
*Encrypt secret with a password and save it in secret store.* *Encrypt secret with a password and save it in secret store.*
- `<secret>` - ethereum secret, 32 bytes long - `<secret>` - ethereum secret, 32 bytes long
- `<password>` - account password, any string - `<password>` - account password, file path
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity - `[--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.* *Change account password.*
- `<address>` - ethereum address, 20 bytes long - `<address>` - ethereum address, 20 bytes long
- `<old-pwd>` - old account password, any string - `<old-pwd>` - old account password, file path
- `<new-pwd>` - new account password, any string - `<new-pwd>` - 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 - `[--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 - `[--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 - `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
```
ethstore import
```
``` ```
0: e6a3d25a7cb7cd21cb720df5b5e8afd154af1bbb 0: e6a3d25a7cb7cd21cb720df5b5e8afd154af1bbb
1: 6edddfc6349aff20bc6467ccf276c5b52487f7a8 1: 6edddfc6349aff20bc6467ccf276c5b52487f7a8
@ -119,15 +125,32 @@ ethstore list
-- --
#### `import-wallet <path> <password> [--dir DIR]`
*Import account from presale wallet.*
- `<path>` - presale wallet path
- `<password>` - 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 <address> <password> [--dir DIR]` #### `remove <address> <password> [--dir DIR]`
*Remove account from secret store.* *Remove account from secret store.*
- `<address>` - ethereum address, 20 bytes long - `<address>` - ethereum address, 20 bytes long
- `<password>` - account password, any string - `<password>` - account password, file path
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity - `[--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.* *Sign message with account's secret.*
- `<address>` - ethereum address, 20 bytes long - `<address>` - ethereum address, 20 bytes long
- `<password>` - account password, any string - `<password>` - account password, file path
- `<message>` - message to sign, 32 bytes long - `<message>` - 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 - `[--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
``` ```
``` ```

View File

@ -18,13 +18,14 @@ extern crate rustc_serialize;
extern crate docopt; extern crate docopt;
extern crate ethstore; extern crate ethstore;
use std::{env, process}; use std::{env, process, fs};
use std::io::Read;
use std::ops::Deref; use std::ops::Deref;
use std::str::FromStr; use std::str::FromStr;
use docopt::Docopt; use docopt::Docopt;
use ethstore::ethkey::{Secret, Address, Message}; use ethstore::ethkey::{Secret, Address, Message};
use ethstore::dir::{KeyDirectory, ParityDirectory, DiskDirectory, GethDirectory, DirectoryType}; 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#" pub const USAGE: &'static str = r#"
Ethereum key management. Ethereum key management.
@ -35,6 +36,7 @@ Usage:
ethstore change-pwd <address> <old-pwd> <new-pwd> [--dir DIR] ethstore change-pwd <address> <old-pwd> <new-pwd> [--dir DIR]
ethstore list [--dir DIR] ethstore list [--dir DIR]
ethstore import [--src DIR] [--dir DIR] ethstore import [--src DIR] [--dir DIR]
ethstore import-wallet <path> <password> [--dir DIR]
ethstore remove <address> <password> [--dir DIR] ethstore remove <address> <password> [--dir DIR]
ethstore sign <address> <password> <message> [--dir DIR] ethstore sign <address> <password> <message> [--dir DIR]
ethstore [-h | --help] ethstore [-h | --help]
@ -53,6 +55,7 @@ Commands:
change-pwd Change password. change-pwd Change password.
list List accounts. list List accounts.
import Import accounts from src. import Import accounts from src.
import-wallet Import presale wallet.
remove Remove account. remove Remove account.
sign Sign message. sign Sign message.
"#; "#;
@ -63,6 +66,7 @@ struct Args {
cmd_change_pwd: bool, cmd_change_pwd: bool,
cmd_list: bool, cmd_list: bool,
cmd_import: bool, cmd_import: bool,
cmd_import_wallet: bool,
cmd_remove: bool, cmd_remove: bool,
cmd_sign: bool, cmd_sign: bool,
arg_secret: String, arg_secret: String,
@ -71,6 +75,7 @@ struct Args {
arg_new_pwd: String, arg_new_pwd: String,
arg_address: String, arg_address: String,
arg_message: String, arg_message: String,
arg_path: String,
flag_src: String, flag_src: String,
flag_dir: String, flag_dir: String,
} }
@ -105,6 +110,15 @@ fn format_accounts(accounts: &[Address]) -> String {
.join("\n") .join("\n")
} }
fn load_password(path: &str) -> Result<String, Error> {
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<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item=S>, S: AsRef<str> { fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item=S>, S: AsRef<str> {
let args: Args = Docopt::new(USAGE) let args: Args = Docopt::new(USAGE)
.and_then(|d| d.argv(command).decode()) .and_then(|d| d.argv(command).decode())
@ -114,11 +128,14 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
return if args.cmd_insert { return if args.cmd_insert {
let secret = try!(Secret::from_str(&args.arg_secret)); let secret = try!(Secret::from_str(&args.arg_secret));
let address = try!(store.insert_account(secret, &args.arg_password)); let password = try!(load_password(&args.arg_password));
let address = try!(store.insert_account(secret, &password));
Ok(format!("{}", address)) Ok(format!("{}", address))
} else if args.cmd_change_pwd { } else if args.cmd_change_pwd {
let address = try!(Address::from_str(&args.arg_address)); let address = try!(Address::from_str(&args.arg_address));
let ok = store.change_password(&address, &args.arg_old_pwd, &args.arg_new_pwd).is_ok(); let old_pwd = try!(load_password(&args.arg_old_pwd));
let new_pwd = try!(load_password(&args.arg_new_pwd));
let ok = store.change_password(&address, &old_pwd, &new_pwd).is_ok();
Ok(format!("{}", ok)) Ok(format!("{}", ok))
} else if args.cmd_list { } else if args.cmd_list {
let accounts = store.accounts(); let accounts = store.accounts();
@ -128,14 +145,22 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
let dst = try!(key_dir(&args.flag_dir)); let dst = try!(key_dir(&args.flag_dir));
let accounts = try!(import_accounts(src.deref(), dst.deref())); let accounts = try!(import_accounts(src.deref(), dst.deref()));
Ok(format_accounts(&accounts)) Ok(format_accounts(&accounts))
} else if args.cmd_import_wallet {
let wallet = try!(PresaleWallet::open(&args.arg_path));
let password = try!(load_password(&args.arg_password));
let kp = try!(wallet.decrypt(&password));
let address = try!(store.insert_account(kp.secret().clone(), &password));
Ok(format!("{}", address))
} else if args.cmd_remove { } else if args.cmd_remove {
let address = try!(Address::from_str(&args.arg_address)); let address = try!(Address::from_str(&args.arg_address));
let ok = store.remove_account(&address, &args.arg_password).is_ok(); let password = try!(load_password(&args.arg_password));
let ok = store.remove_account(&address, &password).is_ok();
Ok(format!("{}", ok)) Ok(format!("{}", ok))
} else if args.cmd_sign { } else if args.cmd_sign {
let address = try!(Address::from_str(&args.arg_address)); let address = try!(Address::from_str(&args.arg_address));
let message = try!(Message::from_str(&args.arg_message)); let message = try!(Message::from_str(&args.arg_message));
let signature = try!(store.sign(&address, &args.arg_password, &message)); let password = try!(load_password(&args.arg_password));
let signature = try!(store.sign(&address, &password, &message));
Ok(format!("{}", signature)) Ok(format!("{}", signature))
} else { } else {
unreachable!(); unreachable!();

View File

@ -67,7 +67,7 @@ impl Keccak256<[u8; 32]> for [u8] {
pub mod aes { pub mod aes {
use rcrypto::blockmodes::{CtrMode, CbcDecryptor, PkcsPadding}; use rcrypto::blockmodes::{CtrMode, CbcDecryptor, PkcsPadding};
use rcrypto::aessafe::{AesSafe128Encryptor, AesSafe128Decryptor}; use rcrypto::aessafe::{AesSafe128Encryptor, AesSafe128Decryptor};
use rcrypto::symmetriccipher::{Encryptor, Decryptor}; use rcrypto::symmetriccipher::{Encryptor, Decryptor, SymmetricCipherError};
use rcrypto::buffer::{RefReadBuffer, RefWriteBuffer}; use rcrypto::buffer::{RefReadBuffer, RefWriteBuffer};
/// Encrypt a message /// Encrypt a message
@ -83,9 +83,10 @@ pub mod aes {
} }
/// Decrypt a message using cbc mode /// 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()); 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(())
} }
} }

View File

@ -43,7 +43,7 @@ impl PresaleWallet {
pbkdf2(&mut h_mac, password.as_bytes(), 2000, &mut derived_key); pbkdf2(&mut h_mac, password.as_bytes(), 2000, &mut derived_key);
let mut key = [0u8; 64]; 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()); let secret = Secret::from(key.keccak256());
if let Ok(kp) = KeyPair::from_secret(secret) { if let Ok(kp) = KeyPair::from_secret(secret) {
@ -58,7 +58,6 @@ impl PresaleWallet {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ethkey::Address;
use super::PresaleWallet; use super::PresaleWallet;
use json; use json;
@ -74,7 +73,7 @@ mod tests {
let wallet = json::PresaleWallet::load(json.as_bytes()).unwrap(); let wallet = json::PresaleWallet::load(json.as_bytes()).unwrap();
let wallet = PresaleWallet::from(wallet); let wallet = PresaleWallet::from(wallet);
let kp = wallet.decrypt("123").unwrap(); assert!(wallet.decrypt("123").is_ok());
assert_eq!(kp.address(), Address::from(wallet.address)); assert!(wallet.decrypt("124").is_err());
} }
} }

View File

@ -25,6 +25,7 @@ Usage:
parity daemon <pid-file> [options] parity daemon <pid-file> [options]
parity account (new | list ) [options] parity account (new | list ) [options]
parity account import <path>... [options] parity account import <path>... [options]
parity wallet import <path> --password FILE [options]
parity import [ <file> ] [options] parity import [ <file> ] [options]
parity export [ <file> ] [options] parity export [ <file> ] [options]
parity signer new-token [options] parity signer new-token [options]
@ -223,6 +224,7 @@ Miscellaneous Options:
pub struct Args { pub struct Args {
pub cmd_daemon: bool, pub cmd_daemon: bool,
pub cmd_account: bool, pub cmd_account: bool,
pub cmd_wallet: bool,
pub cmd_new: bool, pub cmd_new: bool,
pub cmd_list: bool, pub cmd_list: bool,
pub cmd_export: bool, pub cmd_export: bool,

View File

@ -129,6 +129,11 @@ fn execute(conf: Configuration) {
return; return;
} }
if conf.args.cmd_wallet {
execute_wallet_cli(conf);
return;
}
if conf.args.cmd_export { if conf.args.cmd_export {
execute_export(conf); execute_export(conf);
return; 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( fn wait_for_exit(
panic_handler: Arc<PanicHandler>, panic_handler: Arc<PanicHandler>,
_rpc_server: Option<RpcServer>, _rpc_server: Option<RpcServer>,