From 5b05cbb128bc63dd33c6a2a0a6a33f4b2529c2b8 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Sun, 21 Feb 2016 23:23:46 +0300 Subject: [PATCH 01/11] extended keys with accont meta --- ethtools/Cargo.toml | 10 ++++ ethtools/README.md | 1 + ...--3f49624084b67849c7b4e805c5988c21a430f9d9 | 1 + ...--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf | 1 + ethtools/src/geth_keys.rs | 57 +++++++++++++++++++ ethtools/src/lib.rs | 21 +++++++ util/src/hash.rs | 5 +- util/src/keys/directory.rs | 13 ++++- util/src/keys/store.rs | 12 +++- 9 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 ethtools/Cargo.toml create mode 100644 ethtools/README.md create mode 100644 ethtools/res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9 create mode 100644 ethtools/res/geth_keystore/UTC--2016-02-20T09-33-03.984382741Z--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf create mode 100644 ethtools/src/geth_keys.rs create mode 100644 ethtools/src/lib.rs diff --git a/ethtools/Cargo.toml b/ethtools/Cargo.toml new file mode 100644 index 000000000..5529c1d33 --- /dev/null +++ b/ethtools/Cargo.toml @@ -0,0 +1,10 @@ +[package] +description = "Ethcore Ethereum tools" +homepage = "http://ethcore.io" +license = "GPL-3.0" +name = "ethtools" +version = "0.9.99" +authors = ["Ethcore "] + +[dependencies] +ethcore-util = { path = "../util" } diff --git a/ethtools/README.md b/ethtools/README.md new file mode 100644 index 000000000..f3e852ff5 --- /dev/null +++ b/ethtools/README.md @@ -0,0 +1 @@ +# ethtools diff --git a/ethtools/res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9 b/ethtools/res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9 new file mode 100644 index 000000000..a62d3056c --- /dev/null +++ b/ethtools/res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9 @@ -0,0 +1 @@ +{"address":"3f49624084b67849c7b4e805c5988c21a430f9d9","Crypto":{"cipher":"aes-128-ctr","ciphertext":"9f27e3dd4fc73e7103ed61e5493662189a3eb52223ae49e3d1deacc04c889eae","cipherparams":{"iv":"457494bf05f2618c397dc74dbb5181c0"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"db14edb18c41ee7f5ec4397df89c3a2ae4d0af60884c52bb54ce490574f8df33"},"mac":"572d24532438d31fdf513c744a3ff26c933ffda5744ee42bc71661cbe3f2112e"},"id":"62a0ad73-556d-496a-8e1c-0783d30d3ace","version":3} \ No newline at end of file diff --git a/ethtools/res/geth_keystore/UTC--2016-02-20T09-33-03.984382741Z--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf b/ethtools/res/geth_keystore/UTC--2016-02-20T09-33-03.984382741Z--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf new file mode 100644 index 000000000..b6caa1c47 --- /dev/null +++ b/ethtools/res/geth_keystore/UTC--2016-02-20T09-33-03.984382741Z--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf @@ -0,0 +1 @@ +{"address":"5ba4dcf897e97c2bdf8315b9ef26c13c085988cf","Crypto":{"cipher":"aes-128-ctr","ciphertext":"d4a08ec930163778273920f6ad1d49b71836337be6fd9863993ac700a612fddd","cipherparams":{"iv":"89ce5ec129fc27cd5bcbeb8c92bdad50"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"612ab108dc37e69ee8af37a7b24bf7f2234086d7bbf945bacdeccce331f7f84a"},"mac":"4152caa7444e06784223d735cea80cd2690b4c587ad8db3d5529442227b25695"},"id":"35086353-fb12-4029-b56b-033cd61ce35b","version":3} \ No newline at end of file diff --git a/ethtools/src/geth_keys.rs b/ethtools/src/geth_keys.rs new file mode 100644 index 000000000..58dc2a0b1 --- /dev/null +++ b/ethtools/src/geth_keys.rs @@ -0,0 +1,57 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity 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. + +// Parity 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 Parity. If not, see . + +//! Geth keys import/export tool + +use util::hash::*; +use std::path::Path; +use std::result::*; +use std::fs; +use std::str::FromStr; + +/// Enumerates all geth keys in the directory and returns collection of tuples `(accountId, filename)` +pub fn enumerate_geth_keys(path: &Path) -> Result, ::std::io::Error> { + let mut entries = Vec::new(); + for entry in try!(fs::read_dir(path)) { + let entry = try!(entry); + if !try!(fs::metadata(entry.path())).is_dir() { + match entry.file_name().to_str() { + Some(name) => { + let parts: Vec<&str> = name.split("--").collect(); + if parts.len() != 3 { continue; } + match Address::from_str(parts[2]) { + Ok(account_id) => { entries.push((account_id, name.to_owned())); } + Err(e) => { panic!("error: {:?}", e); } + } + }, + None => { continue; } + }; + } + } + Ok(entries) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::path::Path; + + #[test] + fn can_enumerate() { + let keys = enumerate_geth_keys(Path::new("res/geth_keystore")).unwrap(); + assert_eq!(2, keys.len()); + } +} diff --git a/ethtools/src/lib.rs b/ethtools/src/lib.rs new file mode 100644 index 000000000..41dd69cf9 --- /dev/null +++ b/ethtools/src/lib.rs @@ -0,0 +1,21 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity 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. + +// Parity 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 Parity. If not, see . + +//! Ethereum Tools Library + +extern crate ethcore_util as util; + +pub mod geth_keys; diff --git a/util/src/hash.rs b/util/src/hash.rs index 71c690ef6..a6e8f7950 100644 --- a/util/src/hash.rs +++ b/util/src/hash.rs @@ -235,7 +235,7 @@ macro_rules! impl_hash { } impl serde::Serialize for $from { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer { let mut hex = "0x".to_owned(); hex.push_str(self.to_hex().as_ref()); @@ -250,7 +250,7 @@ macro_rules! impl_hash { impl serde::de::Visitor for HashVisitor { type Value = $from; - + fn visit_str(&mut self, value: &str) -> Result where E: serde::Error { // 0x + len if value.len() != 2 + $size * 2 { @@ -719,4 +719,3 @@ mod tests { assert_eq!(r, u); } } - diff --git a/util/src/keys/directory.rs b/util/src/keys/directory.rs index bc875db3f..298f67b99 100644 --- a/util/src/keys/directory.rs +++ b/util/src/keys/directory.rs @@ -333,7 +333,9 @@ pub struct KeyFileContent { /// Holds cypher and decrypt function settings. pub crypto: KeyFileCrypto, /// The identifier. - pub id: Uuid + pub id: Uuid, + /// Account (if present) + pub account: Option
, } #[derive(Debug)] @@ -374,7 +376,8 @@ impl KeyFileContent { KeyFileContent { id: new_uuid(), version: KeyFileVersion::V3(3), - crypto: crypto + crypto: crypto, + account: None } } @@ -407,6 +410,9 @@ impl KeyFileContent { Ok(id) => id }; + let account = as_object.get("account").and_then(|json| json.as_string()).and_then( + |account_text| match Address::from_str(account_text) { Ok(account) => Some(account), Err(_) => None }); + let crypto = match as_object.get("crypto") { None => { return Err(KeyFileParseError::NoCryptoSection); } Some(crypto_json) => match KeyFileCrypto::from_json(crypto_json) { @@ -418,7 +424,8 @@ impl KeyFileContent { Ok(KeyFileContent { version: version, id: id.clone(), - crypto: crypto + crypto: crypto, + account: account }) } diff --git a/util/src/keys/store.rs b/util/src/keys/store.rs index ae44d567a..100f3d30c 100644 --- a/util/src/keys/store.rs +++ b/util/src/keys/store.rs @@ -63,12 +63,22 @@ impl SecretStore { /// new instance of Secret Store pub fn new() -> SecretStore { let mut path = ::std::env::home_dir().expect("Failed to get home dir"); - path.push(".keys"); + path.push("keystore"); SecretStore { directory: KeyDirectory::new(&path) } } + pub fn accounts(&self) -> Result, ::std::io::Error> { + let accounts = try!(self.directory.list()).iter().map(|key_id| self.directory.get(key_id)) + .filter(|key| key.is_some()) + .map(|key| { let some_key = key.unwrap(); (some_key.account, some_key.id) }) + .filter(|&(ref account, _)| account.is_some()) + .map(|(account, id)| (account.unwrap(), id)) + .collect::>(); + Ok(accounts) + } + #[cfg(test)] fn new_test(path: &::devtools::RandomTempPath) -> SecretStore { SecretStore { From 8bc0b7c77c79ee5ba1868e678c502ba0aa026925 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Sun, 21 Feb 2016 23:44:12 +0300 Subject: [PATCH 02/11] import pub --- util/src/keys/store.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/util/src/keys/store.rs b/util/src/keys/store.rs index 100f3d30c..284c78b2c 100644 --- a/util/src/keys/store.rs +++ b/util/src/keys/store.rs @@ -69,6 +69,7 @@ impl SecretStore { } } + /// Lists all accounts and corresponding key ids pub fn accounts(&self) -> Result, ::std::io::Error> { let accounts = try!(self.directory.list()).iter().map(|key_id| self.directory.get(key_id)) .filter(|key| key.is_some()) @@ -79,6 +80,21 @@ impl SecretStore { Ok(accounts) } + /// Resolves key_id by account address + pub fn account(&self, account: &Address) -> Option { + let mut accounts = match self.accounts() { + Ok(accounts) => accounts, + Err(e) => { warn!(target: "sstore", "Failed to load accounts: {}", e); return None; } + }; + accounts.retain(|&(ref store_account, _)| account == store_account); + accounts.first().and_then(|&(_, ref key_id)| Some(key_id.clone())) + } + + pub fn import_key(&mut self, key_file: KeyFileContent) -> Result<(), ::std::io::Error> { + try!(self.directory.save(key_file)); + Ok(()) + } + #[cfg(test)] fn new_test(path: &::devtools::RandomTempPath) -> SecretStore { SecretStore { From 753f52fc22b2f5667dd477cc38eac62e1c667eb3 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Mon, 22 Feb 2016 20:07:56 +0300 Subject: [PATCH 03/11] geth import finish --- ethtools/Cargo.toml | 2 + ...--3f49624084b67849c7b4e805c5988c21a430f9d9 | 22 +++++++- ...--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf | 22 +++++++- ethtools/src/geth_keys.rs | 50 ++++++++++++++++++- ethtools/src/lib.rs | 2 + 5 files changed, 95 insertions(+), 3 deletions(-) diff --git a/ethtools/Cargo.toml b/ethtools/Cargo.toml index 5529c1d33..0ff934421 100644 --- a/ethtools/Cargo.toml +++ b/ethtools/Cargo.toml @@ -8,3 +8,5 @@ authors = ["Ethcore "] [dependencies] ethcore-util = { path = "../util" } +rustc-serialize = "0.3" +ethcore-devtools = { path = "../devtools" } diff --git a/ethtools/res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9 b/ethtools/res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9 index a62d3056c..afc376774 100644 --- a/ethtools/res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9 +++ b/ethtools/res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9 @@ -1 +1,21 @@ -{"address":"3f49624084b67849c7b4e805c5988c21a430f9d9","Crypto":{"cipher":"aes-128-ctr","ciphertext":"9f27e3dd4fc73e7103ed61e5493662189a3eb52223ae49e3d1deacc04c889eae","cipherparams":{"iv":"457494bf05f2618c397dc74dbb5181c0"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"db14edb18c41ee7f5ec4397df89c3a2ae4d0af60884c52bb54ce490574f8df33"},"mac":"572d24532438d31fdf513c744a3ff26c933ffda5744ee42bc71661cbe3f2112e"},"id":"62a0ad73-556d-496a-8e1c-0783d30d3ace","version":3} \ No newline at end of file +{ + "address": "3f49624084b67849c7b4e805c5988c21a430f9d9", + "Crypto": { + "cipher": "aes-128-ctr", + "ciphertext": "9f27e3dd4fc73e7103ed61e5493662189a3eb52223ae49e3d1deacc04c889eae", + "cipherparams": { + "iv": "457494bf05f2618c397dc74dbb5181c0" + }, + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 262144, + "p": 1, + "r": 8, + "salt": "db14edb18c41ee7f5ec4397df89c3a2ae4d0af60884c52bb54ce490574f8df33" + }, + "mac": "572d24532438d31fdf513c744a3ff26c933ffda5744ee42bc71661cbe3f2112e" + }, + "id": "62a0ad73-556d-496a-8e1c-0783d30d3ace", + "version": 3 +} diff --git a/ethtools/res/geth_keystore/UTC--2016-02-20T09-33-03.984382741Z--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf b/ethtools/res/geth_keystore/UTC--2016-02-20T09-33-03.984382741Z--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf index b6caa1c47..b14922037 100644 --- a/ethtools/res/geth_keystore/UTC--2016-02-20T09-33-03.984382741Z--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf +++ b/ethtools/res/geth_keystore/UTC--2016-02-20T09-33-03.984382741Z--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf @@ -1 +1,21 @@ -{"address":"5ba4dcf897e97c2bdf8315b9ef26c13c085988cf","Crypto":{"cipher":"aes-128-ctr","ciphertext":"d4a08ec930163778273920f6ad1d49b71836337be6fd9863993ac700a612fddd","cipherparams":{"iv":"89ce5ec129fc27cd5bcbeb8c92bdad50"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"612ab108dc37e69ee8af37a7b24bf7f2234086d7bbf945bacdeccce331f7f84a"},"mac":"4152caa7444e06784223d735cea80cd2690b4c587ad8db3d5529442227b25695"},"id":"35086353-fb12-4029-b56b-033cd61ce35b","version":3} \ No newline at end of file +{ + "address": "5ba4dcf897e97c2bdf8315b9ef26c13c085988cf", + "Crypto": { + "cipher": "aes-128-ctr", + "ciphertext": "d4a08ec930163778273920f6ad1d49b71836337be6fd9863993ac700a612fddd", + "cipherparams": { + "iv": "89ce5ec129fc27cd5bcbeb8c92bdad50" + }, + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 262144, + "p": 1, + "r": 8, + "salt": "612ab108dc37e69ee8af37a7b24bf7f2234086d7bbf945bacdeccce331f7f84a" + }, + "mac": "4152caa7444e06784223d735cea80cd2690b4c587ad8db3d5529442227b25695" + }, + "id": "35086353-fb12-4029-b56b-033cd61ce35b", + "version": 3 +} diff --git a/ethtools/src/geth_keys.rs b/ethtools/src/geth_keys.rs index 58dc2a0b1..3148407dd 100644 --- a/ethtools/src/geth_keys.rs +++ b/ethtools/src/geth_keys.rs @@ -17,13 +17,18 @@ //! Geth keys import/export tool use util::hash::*; +use util::keys::store::SecretStore; +use util::keys::directory::KeyFileContent; use std::path::Path; use std::result::*; use std::fs; use std::str::FromStr; +use std::io; +use std::io::Read; +use rustc_serialize::json::Json; /// Enumerates all geth keys in the directory and returns collection of tuples `(accountId, filename)` -pub fn enumerate_geth_keys(path: &Path) -> Result, ::std::io::Error> { +pub fn enumerate_geth_keys(path: &Path) -> Result, io::Error> { let mut entries = Vec::new(); for entry in try!(fs::read_dir(path)) { let entry = try!(entry); @@ -44,14 +49,57 @@ pub fn enumerate_geth_keys(path: &Path) -> Result, ::std: Ok(entries) } +#[derive(Debug)] +pub enum ImportError { + IoError(io::Error), + FormatError, +} + +impl From for ImportError { + fn from (err: io::Error) -> ImportError { + ImportError::IoError(err) + } +} + +pub fn import_geth_key(secret_store: &mut SecretStore, geth_keyfile_path: &Path) -> Result<(), ImportError> { + let mut file = try!(fs::File::open(geth_keyfile_path)); + let mut buf = String::new(); + try!(file.read_to_string(&mut buf)); + + let mut json = match Json::from_str(&buf) { + Ok(parsed_json) => try!(parsed_json.as_object().ok_or(ImportError::FormatError)).clone(), + Err(_) => { return Err(ImportError::FormatError); } + }; + let crypto_object = try!(json.get("Crypto").and_then(|crypto| crypto.as_object()).ok_or(ImportError::FormatError)).clone(); + json.insert("crypto".to_owned(), Json::Object(crypto_object.clone())); + json.remove("Crypto"); + match KeyFileContent::load(&Json::Object(json.clone())) { + Ok(key_file) => try!(secret_store.import_key(key_file)), + Err(_) => { return Err(ImportError::FormatError); } + }; + Ok(()) +} + #[cfg(test)] mod tests { use super::*; use std::path::Path; + use util::hash::*; + use util::keys::store::SecretStore; + use std::str::FromStr; #[test] fn can_enumerate() { let keys = enumerate_geth_keys(Path::new("res/geth_keystore")).unwrap(); assert_eq!(2, keys.len()); } + + #[test] + fn can_import() { + let temp = ::devtools::RandomTempPath::new(); + let mut secret_store = SecretStore::new_in(temp.as_path()); + import_geth_key(&mut secret_store, Path::new("res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9")).unwrap(); + let key = secret_store.account(&Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap()); + assert!(key.is_some()); + } } diff --git a/ethtools/src/lib.rs b/ethtools/src/lib.rs index 41dd69cf9..1509f6d81 100644 --- a/ethtools/src/lib.rs +++ b/ethtools/src/lib.rs @@ -17,5 +17,7 @@ //! Ethereum Tools Library extern crate ethcore_util as util; +extern crate rustc_serialize; +extern crate ethcore_devtools as devtools; pub mod geth_keys; From 61a3e687f5b05b11c92dc3cbf98348ce25d864e9 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Mon, 22 Feb 2016 20:08:16 +0300 Subject: [PATCH 04/11] store extra interface --- util/src/keys/directory.rs | 12 +++++++++- util/src/keys/store.rs | 46 +++++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/util/src/keys/directory.rs b/util/src/keys/directory.rs index 298f67b99..e05ae4877 100644 --- a/util/src/keys/directory.rs +++ b/util/src/keys/directory.rs @@ -381,6 +381,16 @@ impl KeyFileContent { } } + pub fn load(json: &Json) -> Result { + match Self::from_json(json) { + Ok(key_file) => Ok(key_file), + Err(e) => { + warn!(target: "sstore", "Error parsing json for key: {:?}", e); + Err(()) + } + } + } + /// Returns key file version if it is known. pub fn version(&self) -> Option { match self.version { @@ -410,7 +420,7 @@ impl KeyFileContent { Ok(id) => id }; - let account = as_object.get("account").and_then(|json| json.as_string()).and_then( + let account = as_object.get("address").and_then(|json| json.as_string()).and_then( |account_text| match Address::from_str(account_text) { Ok(account) => Some(account), Err(_) => None }); let crypto = match as_object.get("crypto") { diff --git a/util/src/keys/store.rs b/util/src/keys/store.rs index 284c78b2c..7aa6d2e76 100644 --- a/util/src/keys/store.rs +++ b/util/src/keys/store.rs @@ -64,8 +64,12 @@ impl SecretStore { pub fn new() -> SecretStore { let mut path = ::std::env::home_dir().expect("Failed to get home dir"); path.push("keystore"); + Self::new_in(&path) + } + + pub fn new_in(path: &Path) -> SecretStore { SecretStore { - directory: KeyDirectory::new(&path) + directory: KeyDirectory::new(path) } } @@ -285,6 +289,25 @@ mod tests { result } + fn pregenerate_accounts(temp: &RandomTempPath, count: usize) -> Vec { + use keys::directory::{KeyFileContent, KeyFileCrypto}; + let mut write_sstore = SecretStore::new_test(&temp); + let mut result = Vec::new(); + for i in 0..count { + let mut key_file = + KeyFileContent::new( + KeyFileCrypto::new_pbkdf2( + FromHex::from_hex("5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46").unwrap(), + H128::from_str("6087dab2f9fdbbfaddc31a909735c1e6").unwrap(), + H256::from_str("ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd").unwrap(), + H256::from_str("517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2").unwrap(), + 262144, + 32)); + key_file.account = Some(x!(i as u64)); + } + result + } + #[test] fn can_get() { let temp = RandomTempPath::create_dir(); @@ -319,5 +342,26 @@ mod tests { assert_eq!(4, sstore.directory.list().unwrap().len()) } + #[test] + fn can_import_account() { + let temp = RandomTempPath::create_dir(); + use keys::directory::{KeyFileContent, KeyFileCrypto}; + let mut key_file = + KeyFileContent::new( + KeyFileCrypto::new_pbkdf2( + FromHex::from_hex("5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46").unwrap(), + H128::from_str("6087dab2f9fdbbfaddc31a909735c1e6").unwrap(), + H256::from_str("ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd").unwrap(), + H256::from_str("517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2").unwrap(), + 262144, + 32)); + key_file.account = Some(Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap()); + let mut sstore = SecretStore::new_test(&temp); + + sstore.import_key(key_file); + + assert_eq!(1, sstore.accounts().unwrap().len()); + assert!(sstore.account(&Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap()).is_some()); + } } From 93f2ee66bc2f9561b31abc68adf04fe00f35a7f5 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Mon, 22 Feb 2016 22:19:33 +0300 Subject: [PATCH 05/11] geth directory import --- ethtools/Cargo.toml | 1 + ethtools/src/geth_keys.rs | 31 +++++++++++++++++++++++++++++-- ethtools/src/lib.rs | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/ethtools/Cargo.toml b/ethtools/Cargo.toml index 0ff934421..87e101b4d 100644 --- a/ethtools/Cargo.toml +++ b/ethtools/Cargo.toml @@ -10,3 +10,4 @@ authors = ["Ethcore "] ethcore-util = { path = "../util" } rustc-serialize = "0.3" ethcore-devtools = { path = "../devtools" } +log = "0.3" diff --git a/ethtools/src/geth_keys.rs b/ethtools/src/geth_keys.rs index 3148407dd..c1a7782d7 100644 --- a/ethtools/src/geth_keys.rs +++ b/ethtools/src/geth_keys.rs @@ -19,7 +19,7 @@ use util::hash::*; use util::keys::store::SecretStore; use util::keys::directory::KeyFileContent; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::result::*; use std::fs; use std::str::FromStr; @@ -61,6 +61,7 @@ impl From for ImportError { } } +/// Imports one geth key to the store pub fn import_geth_key(secret_store: &mut SecretStore, geth_keyfile_path: &Path) -> Result<(), ImportError> { let mut file = try!(fs::File::open(geth_keyfile_path)); let mut buf = String::new(); @@ -80,6 +81,19 @@ pub fn import_geth_key(secret_store: &mut SecretStore, geth_keyfile_path: &Path) Ok(()) } +pub fn import_geth_keys(secret_store: &mut SecretStore, geth_keyfiles_directory: &Path) -> Result<(), ImportError> { + let geth_files = try!(enumerate_geth_keys(geth_keyfiles_directory)); + for &(ref address, ref file_path) in geth_files.iter() { + let mut path = PathBuf::new(); + path.push(geth_keyfiles_directory); + path.push(file_path); + if let Err(e) = import_geth_key(secret_store, Path::new(&path)) { + warn!("Skipped geth address {}, error importing: {:?}", address, e) + } + } + Ok(()) +} + #[cfg(test)] mod tests { use super::*; @@ -96,10 +110,23 @@ mod tests { #[test] fn can_import() { - let temp = ::devtools::RandomTempPath::new(); + let temp = ::devtools::RandomTempPath::create_dir(); let mut secret_store = SecretStore::new_in(temp.as_path()); import_geth_key(&mut secret_store, Path::new("res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9")).unwrap(); let key = secret_store.account(&Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap()); assert!(key.is_some()); } + + #[test] + fn can_import_directory() { + let temp = ::devtools::RandomTempPath::create_dir(); + let mut secret_store = SecretStore::new_in(temp.as_path()); + import_geth_keys(&mut secret_store, Path::new("res/geth_keystore")).unwrap(); + + let key = secret_store.account(&Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap()); + assert!(key.is_some()); + + let key = secret_store.account(&Address::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap()); + assert!(key.is_some()); + } } diff --git a/ethtools/src/lib.rs b/ethtools/src/lib.rs index 1509f6d81..4cf365f5f 100644 --- a/ethtools/src/lib.rs +++ b/ethtools/src/lib.rs @@ -19,5 +19,6 @@ extern crate ethcore_util as util; extern crate rustc_serialize; extern crate ethcore_devtools as devtools; +#[macro_use] extern crate log; pub mod geth_keys; From e604c97a435dd763aec9fad8fbceecd65bfe4cf4 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Mon, 22 Feb 2016 23:12:13 +0300 Subject: [PATCH 06/11] more account tests --- ethtools/src/geth_keys.rs | 18 ++++++++++++ util/src/keys/directory.rs | 47 ++++++++++++++++++++++++++++-- util/src/keys/store.rs | 59 ++++++++++++++++++++++++++------------ 3 files changed, 103 insertions(+), 21 deletions(-) diff --git a/ethtools/src/geth_keys.rs b/ethtools/src/geth_keys.rs index c1a7782d7..7d6c7adef 100644 --- a/ethtools/src/geth_keys.rs +++ b/ethtools/src/geth_keys.rs @@ -49,9 +49,12 @@ pub fn enumerate_geth_keys(path: &Path) -> Result, io::Er Ok(entries) } +/// Geth import error #[derive(Debug)] pub enum ImportError { + /// Io error reading geth file IoError(io::Error), + /// format error FormatError, } @@ -81,6 +84,7 @@ pub fn import_geth_key(secret_store: &mut SecretStore, geth_keyfile_path: &Path) Ok(()) } +/// Imports all geth keys in the directory pub fn import_geth_keys(secret_store: &mut SecretStore, geth_keyfiles_directory: &Path) -> Result<(), ImportError> { let geth_files = try!(enumerate_geth_keys(geth_keyfiles_directory)); for &(ref address, ref file_path) in geth_files.iter() { @@ -129,4 +133,18 @@ mod tests { let key = secret_store.account(&Address::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap()); assert!(key.is_some()); } + + #[test] + fn can_decrypt_with_imported() { + use util::keys::store::EncryptedHashMap; + use util::bytes::*; + + let temp = ::devtools::RandomTempPath::create_dir(); + let mut secret_store = SecretStore::new_in(temp.as_path()); + import_geth_keys(&mut secret_store, Path::new("res/geth_keystore")).unwrap(); + + let val = secret_store.get::(&H128::from_str("62a0ad73556d496a8e1c0783d30d3ace").unwrap(), "123"); + assert!(val.is_ok()); + assert_eq!(vec![0u8, 10], val.unwrap()); + } } diff --git a/util/src/keys/directory.rs b/util/src/keys/directory.rs index e05ae4877..233b8b974 100644 --- a/util/src/keys/directory.rs +++ b/util/src/keys/directory.rs @@ -381,6 +381,7 @@ impl KeyFileContent { } } + /// Loads key from valid json, returns error and records warning if key is mallformed pub fn load(json: &Json) -> Result { match Self::from_json(json) { Ok(key_file) => Ok(key_file), @@ -444,6 +445,7 @@ impl KeyFileContent { map.insert("id".to_owned(), Json::String(uuid_to_string(&self.id))); map.insert("version".to_owned(), Json::U64(CURRENT_DECLARED_VERSION)); map.insert("crypto".to_owned(), self.crypto.to_json()); + if let Some(ref address) = self.account { map.insert("address".to_owned(), Json::String(format!("{:?}", address))); } Json::Object(map) } } @@ -670,7 +672,7 @@ mod file_tests { } #[test] - fn can_read_scrypt_krf() { + fn can_read_scrypt_kdf() { let json = Json::from_str( r#" { @@ -706,6 +708,47 @@ mod file_tests { } } + #[test] + fn can_read_scrypt_kdf_params() { + let json = Json::from_str( + r#" + { + "crypto" : { + "cipher" : "aes-128-ctr", + "cipherparams" : { + "iv" : "83dbcc02d8ccb40e466191a123791e0e" + }, + "ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c", + "kdf" : "scrypt", + "kdfparams" : { + "dklen" : 32, + "n" : 262144, + "r" : 1, + "p" : 8, + "salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19" + }, + "mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097" + }, + "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6", + "version" : 3 + } + "#).unwrap(); + + match KeyFileContent::from_json(&json) { + Ok(key_file) => { + match key_file.crypto.kdf { + KeyFileKdf::Scrypt(scrypt_params) => { + assert_eq!(262144, scrypt_params.n); + assert_eq!(1, scrypt_params.r); + assert_eq!(8, scrypt_params.p); + }, + _ => { panic!("expected kdf params of crypto to be of scrypt type" ); } + } + }, + Err(e) => panic!("Error parsing valid file: {:?}", e) + } + } + #[test] fn can_return_error_no_id() { let json = Json::from_str( @@ -861,7 +904,7 @@ mod file_tests { panic!("Should be error of no identifier, got ok"); }, Err(KeyFileParseError::Crypto(CryptoParseError::Scrypt(_))) => { }, - Err(other_error) => { panic!("should be error of no identifier, got {:?}", other_error); } + Err(other_error) => { panic!("should be scrypt parse error, got {:?}", other_error); } } } diff --git a/util/src/keys/store.rs b/util/src/keys/store.rs index 7aa6d2e76..1fdb1e8a3 100644 --- a/util/src/keys/store.rs +++ b/util/src/keys/store.rs @@ -19,11 +19,12 @@ use keys::directory::*; use common::*; use rcrypto::pbkdf2::*; +use rcrypto::scrypt::*; use rcrypto::hmac::*; use crypto; const KEY_LENGTH: u32 = 32; -const KEY_ITERATIONS: u32 = 4096; +const KEY_ITERATIONS: u32 = 10240; const KEY_LENGTH_AES: u32 = KEY_LENGTH/2; const KEY_LENGTH_USIZE: usize = KEY_LENGTH as usize; @@ -60,13 +61,14 @@ pub struct SecretStore { } impl SecretStore { - /// new instance of Secret Store + /// new instance of Secret Store in default home directory pub fn new() -> SecretStore { let mut path = ::std::env::home_dir().expect("Failed to get home dir"); path.push("keystore"); Self::new_in(&path) } + /// new instance of Secret Store in specific directory pub fn new_in(path: &Path) -> SecretStore { SecretStore { directory: KeyDirectory::new(path) @@ -94,6 +96,7 @@ impl SecretStore { accounts.first().and_then(|&(_, ref key_id)| Some(key_id.clone())) } + /// Imports pregenerated key, returns error if not saved correctly pub fn import_key(&mut self, key_file: KeyFileContent) -> Result<(), ::std::io::Error> { try!(self.directory.save(key_file)); Ok(()) @@ -120,6 +123,15 @@ fn derive_key(password: &str, salt: &H256) -> (Bytes, Bytes) { derive_key_iterations(password, salt, KEY_ITERATIONS) } +fn derive_key_scrypt(password: &str, salt: &H256, n: u32, p: u32, r: u32) -> (Bytes, Bytes) { + let mut derived_key = vec![0u8; KEY_LENGTH_USIZE]; + let scrypt_params = ScryptParams::new(n as u8, r, p); + scrypt(password.as_bytes(), &salt.as_slice(), &scrypt_params, &mut derived_key); + let derived_right_bits = &derived_key[0..KEY_LENGTH_AES_USIZE]; + let derived_left_bits = &derived_key[KEY_LENGTH_AES_USIZE..KEY_LENGTH_USIZE]; + (derived_right_bits.to_vec(), derived_left_bits.to_vec()) +} + fn derive_mac(derived_left_bits: &[u8], cipher_text: &[u8]) -> Bytes { let mut mac = vec![0u8; KEY_LENGTH_AES_USIZE + cipher_text.len()]; mac[0..KEY_LENGTH_AES_USIZE].clone_from_slice(derived_left_bits); @@ -131,24 +143,22 @@ impl EncryptedHashMap for SecretStore { fn get(&self, key: &H128, password: &str) -> Result { match self.directory.get(key) { Some(key_file) => { - let decrypted_bytes = match key_file.crypto.kdf { - KeyFileKdf::Pbkdf2(ref params) => { - let (derived_left_bits, derived_right_bits) = derive_key_iterations(password, ¶ms.salt, params.c); - if derive_mac(&derived_right_bits, &key_file.crypto.cipher_text) - .sha3() != key_file.crypto.mac { return Err(EncryptedHashMapError::InvalidPassword); } - - let mut val = vec![0u8; key_file.crypto.cipher_text.len()]; - match key_file.crypto.cipher_type { - CryptoCipherType::Aes128Ctr(ref iv) => { - crypto::aes::decrypt(&derived_left_bits, &iv.as_slice(), &key_file.crypto.cipher_text, &mut val); - } - } - val - } - _ => { unimplemented!(); } + let (derived_left_bits, derived_right_bits) = match key_file.crypto.kdf { + KeyFileKdf::Pbkdf2(ref params) => derive_key_iterations(password, ¶ms.salt, params.c), + KeyFileKdf::Scrypt(ref params) => derive_key_scrypt(password, ¶ms.salt, params.n, params.p, params.r) }; - match Value::from_bytes(&decrypted_bytes) { + if derive_mac(&derived_right_bits, &key_file.crypto.cipher_text) + .sha3() != key_file.crypto.mac { return Err(EncryptedHashMapError::InvalidPassword); } + + let mut val = vec![0u8; key_file.crypto.cipher_text.len()]; + match key_file.crypto.cipher_type { + CryptoCipherType::Aes128Ctr(ref iv) => { + crypto::aes::decrypt(&derived_left_bits, &iv.as_slice(), &key_file.crypto.cipher_text, &mut val); + } + }; + + match Value::from_bytes(&val) { Ok(value) => Ok(value), Err(bytes_error) => Err(EncryptedHashMapError::InvalidValueFormat(bytes_error)) } @@ -304,6 +314,8 @@ mod tests { 262144, 32)); key_file.account = Some(x!(i as u64)); + result.push(key_file.id.clone()); + write_sstore.import_key(key_file).unwrap(); } result } @@ -359,9 +371,18 @@ mod tests { let mut sstore = SecretStore::new_test(&temp); - sstore.import_key(key_file); + sstore.import_key(key_file).unwrap(); assert_eq!(1, sstore.accounts().unwrap().len()); assert!(sstore.account(&Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap()).is_some()); } + + #[test] + fn can_list_accounts() { + let temp = RandomTempPath::create_dir(); + pregenerate_accounts(&temp, 30); + let sstore = SecretStore::new_test(&temp); + let accounts = sstore.accounts().unwrap(); + assert_eq!(30, accounts.len()); + } } From 4061799e902901772da2171597df0b5d5b0933d2 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Mon, 22 Feb 2016 23:40:38 +0300 Subject: [PATCH 07/11] scrypto decrypt fix --- ethtools/src/geth_keys.rs | 24 +++++++++++++++++++++++- util/src/keys/store.rs | 2 +- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/ethtools/src/geth_keys.rs b/ethtools/src/geth_keys.rs index 7d6c7adef..1a6fb75d4 100644 --- a/ethtools/src/geth_keys.rs +++ b/ethtools/src/geth_keys.rs @@ -134,6 +134,28 @@ mod tests { assert!(key.is_some()); } + #[test] + fn imports_as_scrypt_keys() { + use util::keys::directory::{KeyDirectory, KeyFileKdf}; + let temp = ::devtools::RandomTempPath::create_dir(); + { + let mut secret_store = SecretStore::new_in(temp.as_path()); + import_geth_keys(&mut secret_store, Path::new("res/geth_keystore")).unwrap(); + } + + let key_directory = KeyDirectory::new(&temp.as_path()); + let key_file = key_directory.get(&H128::from_str("62a0ad73556d496a8e1c0783d30d3ace").unwrap()).unwrap(); + + match key_file.crypto.kdf { + KeyFileKdf::Scrypt(scrypt_params) => { + assert_eq!(262144, scrypt_params.n); + assert_eq!(8, scrypt_params.r); + assert_eq!(1, scrypt_params.p); + }, + _ => { panic!("expected kdf params of crypto to be of scrypt type" ); } + } + } + #[test] fn can_decrypt_with_imported() { use util::keys::store::EncryptedHashMap; @@ -145,6 +167,6 @@ mod tests { let val = secret_store.get::(&H128::from_str("62a0ad73556d496a8e1c0783d30d3ace").unwrap(), "123"); assert!(val.is_ok()); - assert_eq!(vec![0u8, 10], val.unwrap()); + assert_eq!(32, val.unwrap().len()); } } diff --git a/util/src/keys/store.rs b/util/src/keys/store.rs index 1fdb1e8a3..b6ae84990 100644 --- a/util/src/keys/store.rs +++ b/util/src/keys/store.rs @@ -125,7 +125,7 @@ fn derive_key(password: &str, salt: &H256) -> (Bytes, Bytes) { fn derive_key_scrypt(password: &str, salt: &H256, n: u32, p: u32, r: u32) -> (Bytes, Bytes) { let mut derived_key = vec![0u8; KEY_LENGTH_USIZE]; - let scrypt_params = ScryptParams::new(n as u8, r, p); + let scrypt_params = ScryptParams::new(n.trailing_zeros() as u8, r, p); scrypt(password.as_bytes(), &salt.as_slice(), &scrypt_params, &mut derived_key); let derived_right_bits = &derived_key[0..KEY_LENGTH_AES_USIZE]; let derived_left_bits = &derived_key[KEY_LENGTH_AES_USIZE..KEY_LENGTH_USIZE]; From bceafe9094eaf21a4a233b76fb9d3df0a6c638f5 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Tue, 23 Feb 2016 00:05:28 +0300 Subject: [PATCH 08/11] fix import statement --- util/src/keys/store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/src/keys/store.rs b/util/src/keys/store.rs index b6ae84990..3c2aef7c3 100644 --- a/util/src/keys/store.rs +++ b/util/src/keys/store.rs @@ -356,8 +356,8 @@ mod tests { #[test] fn can_import_account() { - let temp = RandomTempPath::create_dir(); use keys::directory::{KeyFileContent, KeyFileCrypto}; + let temp = RandomTempPath::create_dir(); let mut key_file = KeyFileContent::new( KeyFileCrypto::new_pbkdf2( From 5bd355e0af9baf2b831c8ebc19e166794b42d5c6 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Wed, 24 Feb 2016 09:55:09 +0300 Subject: [PATCH 09/11] removing extra crate --- ethtools/Cargo.toml | 13 ---------- ethtools/README.md | 1 - ethtools/src/lib.rs | 24 ----------------- ...--3f49624084b67849c7b4e805c5988c21a430f9d9 | 0 ...--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf | 0 util/src/keys/directory.rs | 2 ++ .../src/keys/geth_import.rs | 26 +++++++------------ util/src/keys/mod.rs | 1 + util/src/keys/store.rs | 14 ++++++++++ 9 files changed, 26 insertions(+), 55 deletions(-) delete mode 100644 ethtools/Cargo.toml delete mode 100644 ethtools/README.md delete mode 100644 ethtools/src/lib.rs rename {ethtools => util}/res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9 (100%) rename {ethtools => util}/res/geth_keystore/UTC--2016-02-20T09-33-03.984382741Z--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf (100%) rename ethtools/src/geth_keys.rs => util/src/keys/geth_import.rs (91%) diff --git a/ethtools/Cargo.toml b/ethtools/Cargo.toml deleted file mode 100644 index 87e101b4d..000000000 --- a/ethtools/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -description = "Ethcore Ethereum tools" -homepage = "http://ethcore.io" -license = "GPL-3.0" -name = "ethtools" -version = "0.9.99" -authors = ["Ethcore "] - -[dependencies] -ethcore-util = { path = "../util" } -rustc-serialize = "0.3" -ethcore-devtools = { path = "../devtools" } -log = "0.3" diff --git a/ethtools/README.md b/ethtools/README.md deleted file mode 100644 index f3e852ff5..000000000 --- a/ethtools/README.md +++ /dev/null @@ -1 +0,0 @@ -# ethtools diff --git a/ethtools/src/lib.rs b/ethtools/src/lib.rs deleted file mode 100644 index 4cf365f5f..000000000 --- a/ethtools/src/lib.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity 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. - -// Parity 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 Parity. If not, see . - -//! Ethereum Tools Library - -extern crate ethcore_util as util; -extern crate rustc_serialize; -extern crate ethcore_devtools as devtools; -#[macro_use] extern crate log; - -pub mod geth_keys; diff --git a/ethtools/res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9 b/util/res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9 similarity index 100% rename from ethtools/res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9 rename to util/res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9 diff --git a/ethtools/res/geth_keystore/UTC--2016-02-20T09-33-03.984382741Z--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf b/util/res/geth_keystore/UTC--2016-02-20T09-33-03.984382741Z--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf similarity index 100% rename from ethtools/res/geth_keystore/UTC--2016-02-20T09-33-03.984382741Z--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf rename to util/res/geth_keystore/UTC--2016-02-20T09-33-03.984382741Z--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf diff --git a/util/src/keys/directory.rs b/util/src/keys/directory.rs index 233b8b974..7fe6fb1ee 100644 --- a/util/src/keys/directory.rs +++ b/util/src/keys/directory.rs @@ -618,6 +618,8 @@ impl KeyDirectory { Err(_) => Err(KeyFileLoadError::ParseError(KeyFileParseError::InvalidJson)) } } + + } diff --git a/ethtools/src/geth_keys.rs b/util/src/keys/geth_import.rs similarity index 91% rename from ethtools/src/geth_keys.rs rename to util/src/keys/geth_import.rs index 1a6fb75d4..61df88589 100644 --- a/ethtools/src/geth_keys.rs +++ b/util/src/keys/geth_import.rs @@ -16,16 +16,9 @@ //! Geth keys import/export tool -use util::hash::*; -use util::keys::store::SecretStore; -use util::keys::directory::KeyFileContent; -use std::path::{Path, PathBuf}; -use std::result::*; -use std::fs; -use std::str::FromStr; -use std::io; -use std::io::Read; -use rustc_serialize::json::Json; +use common::*; +use keys::store::SecretStore; +use keys::directory::KeyFileContent; /// Enumerates all geth keys in the directory and returns collection of tuples `(accountId, filename)` pub fn enumerate_geth_keys(path: &Path) -> Result, io::Error> { @@ -86,6 +79,7 @@ pub fn import_geth_key(secret_store: &mut SecretStore, geth_keyfile_path: &Path) /// Imports all geth keys in the directory pub fn import_geth_keys(secret_store: &mut SecretStore, geth_keyfiles_directory: &Path) -> Result<(), ImportError> { + use std::path::PathBuf; let geth_files = try!(enumerate_geth_keys(geth_keyfiles_directory)); for &(ref address, ref file_path) in geth_files.iter() { let mut path = PathBuf::new(); @@ -101,10 +95,9 @@ pub fn import_geth_keys(secret_store: &mut SecretStore, geth_keyfiles_directory: #[cfg(test)] mod tests { use super::*; - use std::path::Path; - use util::hash::*; - use util::keys::store::SecretStore; - use std::str::FromStr; + use common::*; + use keys::store::SecretStore; + #[test] fn can_enumerate() { @@ -136,7 +129,7 @@ mod tests { #[test] fn imports_as_scrypt_keys() { - use util::keys::directory::{KeyDirectory, KeyFileKdf}; + use keys::directory::{KeyDirectory, KeyFileKdf}; let temp = ::devtools::RandomTempPath::create_dir(); { let mut secret_store = SecretStore::new_in(temp.as_path()); @@ -158,8 +151,7 @@ mod tests { #[test] fn can_decrypt_with_imported() { - use util::keys::store::EncryptedHashMap; - use util::bytes::*; + use keys::store::EncryptedHashMap; let temp = ::devtools::RandomTempPath::create_dir(); let mut secret_store = SecretStore::new_in(temp.as_path()); diff --git a/util/src/keys/mod.rs b/util/src/keys/mod.rs index fd52136d7..b9c9dcb08 100644 --- a/util/src/keys/mod.rs +++ b/util/src/keys/mod.rs @@ -18,3 +18,4 @@ pub mod directory; pub mod store; +mod geth_import; diff --git a/util/src/keys/store.rs b/util/src/keys/store.rs index 3c2aef7c3..3b1ca4727 100644 --- a/util/src/keys/store.rs +++ b/util/src/keys/store.rs @@ -75,6 +75,20 @@ impl SecretStore { } } + /// trys to import keys in the known locations + pub fn try_import_existing(&mut self) { + use std::path::PathBuf; + use keys::geth_import; + + let mut import_path = PathBuf::new(); + import_path.push(::std::env::home_dir().expect("Failed to get home dir")); + import_path.push(".ethereum"); + import_path.push("keystore"); + if let Err(e) = geth_import::import_geth_keys(self, &import_path) { + warn!(target: "sstore", "Error retrieving geth keys: {:?}", e) + } + } + /// Lists all accounts and corresponding key ids pub fn accounts(&self) -> Result, ::std::io::Error> { let accounts = try!(self.directory.list()).iter().map(|key_id| self.directory.get(key_id)) From 3750a8964c0424387cdf545a6d1a4f7dd5035147 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Fri, 26 Feb 2016 17:48:50 +0300 Subject: [PATCH 10/11] removed redundant clones --- util/src/keys/geth_import.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/util/src/keys/geth_import.rs b/util/src/keys/geth_import.rs index 61df88589..2ee3c987c 100644 --- a/util/src/keys/geth_import.rs +++ b/util/src/keys/geth_import.rs @@ -63,12 +63,13 @@ pub fn import_geth_key(secret_store: &mut SecretStore, geth_keyfile_path: &Path) let mut buf = String::new(); try!(file.read_to_string(&mut buf)); - let mut json = match Json::from_str(&buf) { - Ok(parsed_json) => try!(parsed_json.as_object().ok_or(ImportError::FormatError)).clone(), + let mut json_result = Json::from_str(&buf); + let mut json = match json_result { + Ok(ref mut parsed_json) => try!(parsed_json.as_object_mut().ok_or(ImportError::FormatError)), Err(_) => { return Err(ImportError::FormatError); } }; let crypto_object = try!(json.get("Crypto").and_then(|crypto| crypto.as_object()).ok_or(ImportError::FormatError)).clone(); - json.insert("crypto".to_owned(), Json::Object(crypto_object.clone())); + json.insert("crypto".to_owned(), Json::Object(crypto_object)); json.remove("Crypto"); match KeyFileContent::load(&Json::Object(json.clone())) { Ok(key_file) => try!(secret_store.import_key(key_file)), From 52faf8164d36d627bbdfde3f634ada216bf8e525 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 26 Feb 2016 21:38:05 +0100 Subject: [PATCH 11/11] Update store.rs --- util/src/keys/store.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/src/keys/store.rs b/util/src/keys/store.rs index 3b1ca4727..c4fa377f9 100644 --- a/util/src/keys/store.rs +++ b/util/src/keys/store.rs @@ -64,7 +64,8 @@ impl SecretStore { /// new instance of Secret Store in default home directory pub fn new() -> SecretStore { let mut path = ::std::env::home_dir().expect("Failed to get home dir"); - path.push("keystore"); + path.push(".parity"); + path.push("keys"); Self::new_in(&path) }