262 lines
8.3 KiB
Rust
262 lines
8.3 KiB
Rust
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
//! Geth keys import/export tool
|
|
|
|
use common::*;
|
|
use keys::store::SecretStore;
|
|
use keys::directory::KeyFileContent;
|
|
use std::path::PathBuf;
|
|
use path;
|
|
|
|
/// Enumerates all geth keys in the directory and returns collection of tuples `(accountId, filename)`
|
|
pub fn enumerate_geth_keys(path: &Path) -> Result<Vec<(Address, String)>, ImportError> {
|
|
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; }
|
|
let account_id = try!(Address::from_str(parts[2]).map_err(|_| ImportError::Format));
|
|
entries.push((account_id, name.to_owned()));
|
|
},
|
|
None => { continue; }
|
|
};
|
|
}
|
|
}
|
|
Ok(entries)
|
|
}
|
|
|
|
/// Geth import error
|
|
#[derive(Debug)]
|
|
pub enum ImportError {
|
|
/// Io error reading geth file
|
|
Io(io::Error),
|
|
/// format error
|
|
Format,
|
|
}
|
|
|
|
impl From<io::Error> for ImportError {
|
|
fn from (err: io::Error) -> ImportError {
|
|
ImportError::Io(err)
|
|
}
|
|
}
|
|
|
|
/// 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();
|
|
try!(file.read_to_string(&mut buf));
|
|
|
|
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::Format)),
|
|
Err(_) => { return Err(ImportError::Format); }
|
|
};
|
|
if let Some(crypto_object) = json.get("Crypto").and_then(|crypto| crypto.as_object()).cloned() {
|
|
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)),
|
|
Err(_) => { return Err(ImportError::Format); }
|
|
};
|
|
Ok(())
|
|
}
|
|
|
|
/// Imports all geth keys in the directory
|
|
pub fn import_geth_keys(secret_store: &mut SecretStore, geth_keyfiles_directory: &Path) -> Result<usize, ImportError> {
|
|
use std::path::PathBuf;
|
|
let geth_files = try!(enumerate_geth_keys(geth_keyfiles_directory));
|
|
let mut total = 0;
|
|
for &(ref address, ref file_path) in &geth_files {
|
|
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)
|
|
}
|
|
else { total = total + 1}
|
|
}
|
|
Ok(total)
|
|
}
|
|
|
|
|
|
/// Gets the default geth keystore directory.
|
|
pub fn keystore_dir(is_testnet: bool) -> PathBuf {
|
|
path::ethereum::with_default(if is_testnet {"testnet/keystore"} else {"keystore"})
|
|
}
|
|
|
|
/// Imports key(s) from provided file/directory
|
|
pub fn import_keys_path(secret_store: &mut SecretStore, path: &str) -> Result<usize, ImportError> {
|
|
// check if it is just one file or directory
|
|
if let Ok(meta) = fs::metadata(path) {
|
|
if meta.is_file() {
|
|
try!(import_geth_key(secret_store, Path::new(path)));
|
|
return Ok(1);
|
|
}
|
|
else if meta.is_dir() {
|
|
return Ok(try!(fs::read_dir(path)).fold(
|
|
0,
|
|
|total, p|
|
|
total +
|
|
match p {
|
|
Ok(dir_entry) => import_keys_path(secret_store, dir_entry.path().to_str().unwrap()).unwrap_or_else(|_| 0),
|
|
Err(e) => { warn!("Error importing dir entry: {:?}", e); 0 },
|
|
}
|
|
))
|
|
}
|
|
}
|
|
Ok(0)
|
|
}
|
|
|
|
/// Imports all keys from list of provided files/directories
|
|
pub fn import_keys_paths(secret_store: &mut SecretStore, path: &[String]) -> Result<usize, ImportError> {
|
|
Ok(path.iter().fold(0, |total, ref p| total + import_keys_path(secret_store, &p).unwrap_or_else(|_| 0)))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use common::*;
|
|
use keys::store::SecretStore;
|
|
|
|
fn test_path() -> &'static str {
|
|
match ::std::fs::metadata("res") {
|
|
Ok(_) => "res/geth_keystore",
|
|
Err(_) => "util/res/geth_keystore"
|
|
}
|
|
}
|
|
|
|
fn pat_path() -> &'static str {
|
|
match ::std::fs::metadata("res") {
|
|
Ok(_) => "res/pat",
|
|
Err(_) => "util/res/pat"
|
|
}
|
|
}
|
|
|
|
fn test_path_param(param_val: &'static str) -> String {
|
|
test_path().to_owned() + param_val
|
|
}
|
|
|
|
fn pat_path_param(param_val: &'static str) -> String {
|
|
pat_path().to_owned() + param_val
|
|
}
|
|
|
|
#[test]
|
|
fn can_enumerate() {
|
|
let keys = enumerate_geth_keys(Path::new(test_path())).unwrap();
|
|
assert_eq!(3, keys.len());
|
|
}
|
|
|
|
#[test]
|
|
fn can_import_geth_old() {
|
|
let temp = ::devtools::RandomTempPath::create_dir();
|
|
let mut secret_store = SecretStore::new_in(temp.as_path());
|
|
import_geth_key(&mut secret_store, Path::new(&test_path_param("/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_geth140() {
|
|
let temp = ::devtools::RandomTempPath::create_dir();
|
|
let mut secret_store = SecretStore::new_in(temp.as_path());
|
|
import_geth_key(&mut secret_store, Path::new(&test_path_param("/UTC--2016-04-03T08-58-49.834202900Z--63121b431a52f8043c16fcf0d1df9cb7b5f66649"))).unwrap();
|
|
let key = secret_store.account(&Address::from_str("63121b431a52f8043c16fcf0d1df9cb7b5f66649").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(test_path())).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());
|
|
}
|
|
|
|
#[test]
|
|
fn imports_as_scrypt_keys() {
|
|
use 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(test_path())).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]
|
|
#[cfg(feature="heavy-tests")]
|
|
fn can_decrypt_with_imported() {
|
|
use keys::store::EncryptedHashMap;
|
|
|
|
let temp = ::devtools::RandomTempPath::create_dir();
|
|
let mut secret_store = SecretStore::new_in(temp.as_path());
|
|
import_geth_keys(&mut secret_store, Path::new(test_path())).unwrap();
|
|
|
|
let val = secret_store.get::<Bytes>(&H128::from_str("62a0ad73556d496a8e1c0783d30d3ace").unwrap(), "123");
|
|
assert!(val.is_ok());
|
|
assert_eq!(32, val.unwrap().len());
|
|
}
|
|
|
|
#[test]
|
|
fn can_import_by_filename() {
|
|
let temp = ::devtools::RandomTempPath::create_dir();
|
|
let mut secret_store = SecretStore::new_in(temp.as_path());
|
|
|
|
let amount = import_keys_path(&mut secret_store, &pat_path_param("/p1.json")).unwrap();
|
|
assert_eq!(1, amount);
|
|
}
|
|
|
|
#[test]
|
|
fn can_import_by_dir() {
|
|
let temp = ::devtools::RandomTempPath::create_dir();
|
|
let mut secret_store = SecretStore::new_in(temp.as_path());
|
|
|
|
let amount = import_keys_path(&mut secret_store, pat_path()).unwrap();
|
|
assert_eq!(2, amount);
|
|
}
|
|
|
|
#[test]
|
|
fn can_import_mulitple() {
|
|
let temp = ::devtools::RandomTempPath::create_dir();
|
|
let mut secret_store = SecretStore::new_in(temp.as_path());
|
|
|
|
let amount = import_keys_paths(&mut secret_store, &[pat_path_param("/p1.json"), pat_path_param("/p2.json")]).unwrap();
|
|
assert_eq!(2, amount);
|
|
}
|
|
|
|
}
|