Pyethereum keystore support (#9710)

* support for keystore format produced by pyethereum lib + some debug msgs

* made salt unbound also for Scrypt

* made address optional and skip if its none

* ignore values of any type, not just i32

* WIP: local deps

* cleanup

* enable optional address + more cleanup

* yet another cleanup

* enable keystore wo address import (using passwd)

* cleanup

* doc enchancement

* parity/account fix

* redesign after review

* fix indentation

* ignore just version in json under crypto

* remove unnecessary borrowing within match str

Co-Authored-By: tworec <tworec@golem.network>

* remove another unnecessary borrowing

Co-Authored-By: tworec <tworec@golem.network>

* default derived instead of implementing

* log dependency removed

* [ethstore] warn restored + env_logger initialized in CLI

* applied suggestion from @tomusdrw
This commit is contained in:
Piotr Chromiec 2019-01-03 14:07:27 +01:00 committed by Niklas Adolfsson
parent b5f510ead7
commit 469f9c26e7
14 changed files with 107 additions and 54 deletions

11
Cargo.lock generated
View File

@ -708,7 +708,7 @@ dependencies = [
"ethereum-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethjson 0.1.0", "ethjson 0.1.0",
"ethkey 0.3.0", "ethkey 0.3.0",
"ethstore 0.2.0", "ethstore 0.2.1",
"evm 0.1.0", "evm 0.1.0",
"fake-hardware-wallet 0.0.1", "fake-hardware-wallet 0.0.1",
"hardware-wallet 1.12.0", "hardware-wallet 1.12.0",
@ -1143,7 +1143,7 @@ dependencies = [
[[package]] [[package]]
name = "ethstore" name = "ethstore"
version = "0.2.0" version = "0.2.1"
dependencies = [ dependencies = [
"dir 0.1.2", "dir 0.1.2",
"ethereum-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1168,11 +1168,12 @@ dependencies = [
[[package]] [[package]]
name = "ethstore-cli" name = "ethstore-cli"
version = "0.1.0" version = "0.1.1"
dependencies = [ dependencies = [
"dir 0.1.2", "dir 0.1.2",
"docopt 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethstore 0.2.0", "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)",
"ethstore 0.2.1",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"panic_hook 0.1.0", "panic_hook 0.1.0",
"parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2526,7 +2527,7 @@ dependencies = [
"ethereum-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethjson 0.1.0", "ethjson 0.1.0",
"ethkey 0.3.0", "ethkey 0.3.0",
"ethstore 0.2.0", "ethstore 0.2.1",
"fake-fetch 0.0.1", "fake-fetch 0.0.1",
"fake-hardware-wallet 0.0.1", "fake-hardware-wallet 0.0.1",
"fastmap 0.1.0", "fastmap 0.1.0",

View File

@ -168,7 +168,7 @@ fn main() {
Ok(ok) => println!("{}", ok), Ok(ok) => println!("{}", ok),
Err(Error::Docopt(ref e)) => e.exit(), Err(Error::Docopt(ref e)) => e.exit(),
Err(err) => { Err(err) => {
println!("{}", err); eprintln!("{}", err);
process::exit(1); process::exit(1);
} }
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ethstore" name = "ethstore"
version = "0.2.0" version = "0.2.1"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
[dependencies] [dependencies]

View File

@ -1,10 +1,11 @@
[package] [package]
name = "ethstore-cli" name = "ethstore-cli"
version = "0.1.0" version = "0.1.1"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
[dependencies] [dependencies]
docopt = "1.0" docopt = "1.0"
env_logger = "0.5"
num_cpus = "1.6" num_cpus = "1.6"
rustc-hex = "1.0" rustc-hex = "1.0"
serde = "1.0" serde = "1.0"

View File

@ -23,6 +23,8 @@ extern crate parking_lot;
extern crate rustc_hex; extern crate rustc_hex;
extern crate serde; extern crate serde;
extern crate env_logger;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
@ -45,7 +47,7 @@ Usage:
ethstore insert <secret> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD] ethstore insert <secret> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore change-pwd <address> <old-pwd> <new-pwd> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD] ethstore change-pwd <address> <old-pwd> <new-pwd> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore list [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD] ethstore list [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore import [--src DIR] [--dir DIR] ethstore import [<password>] [--src DIR] [--dir DIR]
ethstore import-wallet <path> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD] ethstore import-wallet <path> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
ethstore find-wallet-pass <path> <password> ethstore find-wallet-pass <path> <password>
ethstore remove <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD] ethstore remove <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
@ -146,30 +148,34 @@ impl fmt::Display for Error {
fn main() { fn main() {
panic_hook::set_abort(); 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()) { match execute(env::args()) {
Ok(result) => println!("{}", result), Ok(result) => println!("{}", result),
Err(Error::Docopt(ref e)) => e.exit(), Err(Error::Docopt(ref e)) => e.exit(),
Err(err) => { Err(err) => {
println!("{}", err); eprintln!("{}", err);
process::exit(1); process::exit(1);
} }
} }
} }
fn key_dir(location: &str) -> Result<Box<KeyDirectory>, Error> { fn key_dir(location: &str, password: Option<Password>) -> Result<Box<KeyDirectory>, Error> {
let dir: Box<KeyDirectory> = match location { let dir: RootDiskDirectory = match location {
"geth" => Box::new(RootDiskDirectory::create(dir::geth(false))?), "geth" => RootDiskDirectory::create(dir::geth(false))?,
"geth-test" => Box::new(RootDiskDirectory::create(dir::geth(true))?), "geth-test" => RootDiskDirectory::create(dir::geth(true))?,
path if path.starts_with("parity") => { path if path.starts_with("parity") => {
let chain = path.split('-').nth(1).unwrap_or("ethereum"); let chain = path.split('-').nth(1).unwrap_or("ethereum");
let path = dir::parity(chain); let path = dir::parity(chain);
Box::new(RootDiskDirectory::create(path)?) RootDiskDirectory::create(path)?
}, },
path => Box::new(RootDiskDirectory::create(path)?), path => RootDiskDirectory::create(path)?,
}; };
Ok(dir) Ok(Box::new(dir.with_password(password)))
} }
fn open_args_vault(store: &EthStore, args: &Args) -> Result<SecretVaultRef, Error> { fn open_args_vault(store: &EthStore, args: &Args) -> Result<SecretVaultRef, Error> {
@ -202,9 +208,9 @@ fn format_vaults(vaults: &[String]) -> String {
} }
fn load_password(path: &str) -> Result<Password, Error> { fn load_password(path: &str) -> Result<Password, Error> {
let mut file = fs::File::open(path).map_err(|e| ethstore::Error::Custom(format!("Error opening password file {}: {}", path, e)))?; 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(); let mut password = String::new();
file.read_to_string(&mut password).map_err(|e| ethstore::Error::Custom(format!("Error reading password file {}: {}", path, e)))?; file.read_to_string(&mut password).map_err(|e| ethstore::Error::Custom(format!("Error reading password file '{}': {}", path, e)))?;
// drop EOF // drop EOF
let _ = password.pop(); let _ = password.pop();
Ok(password.into()) Ok(password.into())
@ -214,7 +220,7 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
let args: Args = Docopt::new(USAGE) let args: Args = Docopt::new(USAGE)
.and_then(|d| d.argv(command).deserialize())?; .and_then(|d| d.argv(command).deserialize())?;
let store = EthStore::open(key_dir(&args.flag_dir)?)?; let store = EthStore::open(key_dir(&args.flag_dir, None)?)?;
return if args.cmd_insert { return if args.cmd_insert {
let secret = args.arg_secret.parse().map_err(|_| ethstore::Error::InvalidSecret)?; let secret = args.arg_secret.parse().map_err(|_| ethstore::Error::InvalidSecret)?;
@ -239,8 +245,13 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
.collect(); .collect();
Ok(format_accounts(&accounts)) Ok(format_accounts(&accounts))
} else if args.cmd_import { } else if args.cmd_import {
let src = key_dir(&args.flag_src)?; let password = match args.arg_password.as_ref() {
let dst = key_dir(&args.flag_dir)?; "" => 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)?; let accounts = import_accounts(&*src, &*dst)?;
Ok(format_accounts(&accounts)) Ok(format_accounts(&accounts))
} else if args.cmd_import_wallet { } else if args.cmd_import_wallet {

View File

@ -84,7 +84,8 @@ impl Crypto {
// two parts of derived key // two parts of derived key
// DK = [ DK[0..15] DK[16..31] ] = [derived_left_bits, derived_right_bits] // DK = [ DK[0..15] DK[16..31] ] = [derived_left_bits, derived_right_bits]
let (derived_left_bits, derived_right_bits) = crypto::derive_key_iterations(password.as_bytes(), &salt, iterations); let (derived_left_bits, derived_right_bits) =
crypto::derive_key_iterations(password.as_bytes(), &salt, iterations);
// preallocated (on-stack in case of `Secret`) buffer to hold cipher // preallocated (on-stack in case of `Secret`) buffer to hold cipher
// length = length(plain) as we are using CTR-approach // length = length(plain) as we are using CTR-approach
@ -104,7 +105,7 @@ impl Crypto {
ciphertext: ciphertext.into_vec(), ciphertext: ciphertext.into_vec(),
kdf: Kdf::Pbkdf2(Pbkdf2 { kdf: Kdf::Pbkdf2(Pbkdf2 {
dklen: crypto::KEY_LENGTH as u32, dklen: crypto::KEY_LENGTH as u32,
salt: salt, salt: salt.to_vec(),
c: iterations, c: iterations,
prf: Prf::HmacSha256, prf: Prf::HmacSha256,
}), }),

View File

@ -26,7 +26,7 @@ pub struct Pbkdf2 {
pub c: u32, pub c: u32,
pub dklen: u32, pub dklen: u32,
pub prf: Prf, pub prf: Prf,
pub salt: [u8; 32], pub salt: Vec<u8>,
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@ -35,7 +35,7 @@ pub struct Scrypt {
pub p: u32, pub p: u32,
pub n: u32, pub n: u32,
pub r: u32, pub r: u32,
pub salt: [u8; 32], pub salt: Vec<u8>,
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]

View File

@ -45,7 +45,7 @@ impl Into<json::KeyFile> for SafeAccount {
json::KeyFile { json::KeyFile {
id: From::from(self.id), id: From::from(self.id),
version: self.version.into(), version: self.version.into(),
address: self.address.into(), address: Some(self.address.into()),
crypto: self.crypto.into(), crypto: self.crypto.into(),
name: Some(self.name.into()), name: Some(self.name.into()),
meta: Some(self.meta.into()), meta: Some(self.meta.into()),
@ -77,16 +77,43 @@ impl SafeAccount {
/// Create a new `SafeAccount` from the given `json`; if it was read from a /// Create a new `SafeAccount` from the given `json`; if it was read from a
/// file, the `filename` should be `Some` name. If it is as yet anonymous, then it /// file, the `filename` should be `Some` name. If it is as yet anonymous, then it
/// can be left `None`. /// can be left `None`.
pub fn from_file(json: json::KeyFile, filename: Option<String>) -> Self { /// In case `password` is provided, we will attempt to read the secret from the keyfile
SafeAccount { /// and derive the address from it instead of reading it directly.
/// Providing password is required for `json::KeyFile`s with no address.
pub fn from_file(json: json::KeyFile, filename: Option<String>, password: &Option<Password>) -> Result<Self, Error> {
let crypto = Crypto::from(json.crypto);
let address = match (password, &json.address) {
(None, Some(json_address)) => json_address.into(),
(None, None) => Err(Error::Custom(
"This keystore does not contain address. You need to provide password to import it".into()))?,
(Some(password), json_address) => {
let derived_address = KeyPair::from_secret(
crypto.secret(&password).map_err(|_| Error::InvalidPassword)?
)?.address();
match json_address {
Some(json_address) => {
let json_address = json_address.into();
if derived_address != json_address {
warn!("Detected address mismatch when opening an account. Derived: {:?}, in json got: {:?}",
derived_address, json_address);
}
},
_ => {},
}
derived_address
}
};
Ok(SafeAccount {
id: json.id.into(), id: json.id.into(),
version: json.version.into(), version: json.version.into(),
address: json.address.into(), address,
crypto: json.crypto.into(), crypto,
filename: filename, filename,
name: json.name.unwrap_or(String::new()), name: json.name.unwrap_or(String::new()),
meta: json.meta.unwrap_or("{}".to_owned()), meta: json.meta.unwrap_or("{}".to_owned()),
} })
} }
/// Create a new `SafeAccount` from the given vault `json`; if it was read from a /// Create a new `SafeAccount` from the given vault `json`; if it was read from a
@ -97,14 +124,14 @@ impl SafeAccount {
let meta_plain = meta_crypto.decrypt(password)?; let meta_plain = meta_crypto.decrypt(password)?;
let meta_plain = json::VaultKeyMeta::load(&meta_plain).map_err(|e| Error::Custom(format!("{:?}", e)))?; let meta_plain = json::VaultKeyMeta::load(&meta_plain).map_err(|e| Error::Custom(format!("{:?}", e)))?;
Ok(SafeAccount::from_file(json::KeyFile { SafeAccount::from_file(json::KeyFile {
id: json.id, id: json.id,
version: json.version, version: json.version,
crypto: json.crypto, crypto: json.crypto,
address: meta_plain.address, address: Some(meta_plain.address),
name: meta_plain.name, name: meta_plain.name,
meta: meta_plain.meta, meta: meta_plain.meta,
}, filename)) }, filename, &None)
} }
/// Create a new `VaultKeyFile` from the given `self` /// Create a new `VaultKeyFile` from the given `self`

View File

@ -23,6 +23,7 @@ use {json, SafeAccount, Error};
use json::Uuid; use json::Uuid;
use super::{KeyDirectory, VaultKeyDirectory, VaultKeyDirectoryProvider, VaultKey}; use super::{KeyDirectory, VaultKeyDirectory, VaultKeyDirectoryProvider, VaultKey};
use super::vault::{VAULT_FILE_NAME, VaultDiskDirectory}; use super::vault::{VAULT_FILE_NAME, VaultDiskDirectory};
use ethkey::Password;
const IGNORED_FILES: &'static [&'static str] = &[ const IGNORED_FILES: &'static [&'static str] = &[
"thumbs.db", "thumbs.db",
@ -106,6 +107,7 @@ pub type RootDiskDirectory = DiskDirectory<DiskKeyFileManager>;
pub trait KeyFileManager: Send + Sync { pub trait KeyFileManager: Send + Sync {
/// Read `SafeAccount` from given key file stream /// Read `SafeAccount` from given key file stream
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read; fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read;
/// Write `SafeAccount` to given key file stream /// Write `SafeAccount` to given key file stream
fn write<T>(&self, account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write; fn write<T>(&self, account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write;
} }
@ -117,7 +119,10 @@ pub struct DiskDirectory<T> where T: KeyFileManager {
} }
/// Keys file manager for root keys directory /// Keys file manager for root keys directory
pub struct DiskKeyFileManager; #[derive(Default)]
pub struct DiskKeyFileManager {
password: Option<Password>,
}
impl RootDiskDirectory { impl RootDiskDirectory {
pub fn create<P>(path: P) -> Result<Self, Error> where P: AsRef<Path> { pub fn create<P>(path: P) -> Result<Self, Error> where P: AsRef<Path> {
@ -125,8 +130,14 @@ impl RootDiskDirectory {
Ok(Self::at(path)) Ok(Self::at(path))
} }
/// allows to read keyfiles with given password (needed for keyfiles w/o address)
pub fn with_password(&self, password: Option<Password>) -> Self {
DiskDirectory::new(&self.path, DiskKeyFileManager { password })
}
pub fn at<P>(path: P) -> Self where P: AsRef<Path> { pub fn at<P>(path: P) -> Self where P: AsRef<Path> {
DiskDirectory::new(path, DiskKeyFileManager) DiskDirectory::new(path, DiskKeyFileManager::default())
} }
} }
@ -319,7 +330,7 @@ impl<T> VaultKeyDirectoryProvider for DiskDirectory<T> where T: KeyFileManager {
impl KeyFileManager for DiskKeyFileManager { impl KeyFileManager for DiskKeyFileManager {
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read { fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read {
let key_file = json::KeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?; let key_file = json::KeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?;
Ok(SafeAccount::from_file(key_file, filename)) SafeAccount::from_file(key_file, filename, &self.password)
} }
fn write<T>(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write { fn write<T>(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write {

View File

@ -168,7 +168,7 @@ impl SecretStore for EthStore {
fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &Password, gen_id: bool) -> Result<StoreAccountRef, Error> { fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &Password, gen_id: bool) -> Result<StoreAccountRef, Error> {
let json_keyfile = json::KeyFile::load(json).map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned()))?; let json_keyfile = json::KeyFile::load(json).map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned()))?;
let mut safe_account = SafeAccount::from_file(json_keyfile, None); let mut safe_account = SafeAccount::from_file(json_keyfile, None, &None)?;
if gen_id { if gen_id {
safe_account.id = Random::random(); safe_account.id = Random::random();

View File

@ -25,7 +25,7 @@ use Error;
/// Import an account from a file. /// Import an account from a file.
pub fn import_account(path: &Path, dst: &KeyDirectory) -> Result<Address, Error> { pub fn import_account(path: &Path, dst: &KeyDirectory) -> Result<Address, Error> {
let key_manager = DiskKeyFileManager; let key_manager = DiskKeyFileManager::default();
let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::<HashSet<_>>(); let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::<HashSet<_>>();
let filename = path.file_name().and_then(|n| n.to_str()).map(|f| f.to_owned()); let filename = path.file_name().and_then(|n| n.to_str()).map(|f| f.to_owned());
let account = fs::File::open(&path) let account = fs::File::open(&path)
@ -42,7 +42,9 @@ pub fn import_account(path: &Path, dst: &KeyDirectory) -> Result<Address, Error>
/// Import all accounts from one directory to the other. /// Import all accounts from one directory to the other.
pub fn import_accounts(src: &KeyDirectory, dst: &KeyDirectory) -> Result<Vec<Address>, Error> { pub fn import_accounts(src: &KeyDirectory, dst: &KeyDirectory) -> Result<Vec<Address>, Error> {
let accounts = src.load()?; let accounts = src.load()?;
let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::<HashSet<_>>(); let existing_accounts = dst.load()?.into_iter()
.map(|a| a.address)
.collect::<HashSet<_>>();
accounts.into_iter() accounts.into_iter()
.filter(|a| !existing_accounts.contains(&a.address)) .filter(|a| !existing_accounts.contains(&a.address))

View File

@ -52,6 +52,7 @@ enum CryptoField {
Kdf, Kdf,
KdfParams, KdfParams,
Mac, Mac,
Version,
} }
impl<'a> Deserialize<'a> for CryptoField { impl<'a> Deserialize<'a> for CryptoField {
@ -81,6 +82,7 @@ impl<'a> Visitor<'a> for CryptoFieldVisitor {
"kdf" => Ok(CryptoField::Kdf), "kdf" => Ok(CryptoField::Kdf),
"kdfparams" => Ok(CryptoField::KdfParams), "kdfparams" => Ok(CryptoField::KdfParams),
"mac" => Ok(CryptoField::Mac), "mac" => Ok(CryptoField::Mac),
"version" => Ok(CryptoField::Version),
_ => Err(Error::custom(format!("Unknown field: '{}'", value))), _ => Err(Error::custom(format!("Unknown field: '{}'", value))),
} }
} }
@ -122,6 +124,8 @@ impl<'a> Visitor<'a> for CryptoVisitor {
Some(CryptoField::Kdf) => { kdf = Some(visitor.next_value()?); } Some(CryptoField::Kdf) => { kdf = Some(visitor.next_value()?); }
Some(CryptoField::KdfParams) => { kdfparams = Some(visitor.next_value()?); } Some(CryptoField::KdfParams) => { kdfparams = Some(visitor.next_value()?); }
Some(CryptoField::Mac) => { mac = Some(visitor.next_value()?); } Some(CryptoField::Mac) => { mac = Some(visitor.next_value()?); }
// skip not required version field (it appears in pyethereum generated keystores)
Some(CryptoField::Version) => { visitor.next_value().unwrap_or(()) }
None => { break; } None => { break; }
} }
} }

View File

@ -17,7 +17,7 @@
use std::fmt; use std::fmt;
use serde::{Serialize, Serializer, Deserialize, Deserializer}; use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::{Visitor, Error as SerdeError}; use serde::de::{Visitor, Error as SerdeError};
use super::{Error, H256}; use super::{Error, Bytes};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum KdfSer { pub enum KdfSer {
@ -111,7 +111,7 @@ pub struct Pbkdf2 {
pub c: u32, pub c: u32,
pub dklen: u32, pub dklen: u32,
pub prf: Prf, pub prf: Prf,
pub salt: H256, pub salt: Bytes,
} }
#[derive(Debug, PartialEq, Serialize, Deserialize)] #[derive(Debug, PartialEq, Serialize, Deserialize)]
@ -120,7 +120,7 @@ pub struct Scrypt {
pub p: u32, pub p: u32,
pub n: u32, pub n: u32,
pub r: u32, pub r: u32,
pub salt: H256, pub salt: Bytes,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]

View File

@ -46,7 +46,7 @@ pub struct KeyFile {
pub id: Uuid, pub id: Uuid,
pub version: Version, pub version: Version,
pub crypto: Crypto, pub crypto: Crypto,
pub address: H160, pub address: Option<H160>,
pub name: Option<String>, pub name: Option<String>,
pub meta: Option<String>, pub meta: Option<String>,
} }
@ -158,11 +158,6 @@ impl<'a> Visitor<'a> for KeyFileVisitor {
None => return Err(V::Error::missing_field("crypto")), None => return Err(V::Error::missing_field("crypto")),
}; };
let address = match address {
Some(address) => address,
None => return Err(V::Error::missing_field("address")),
};
let result = KeyFile { let result = KeyFile {
id: id, id: id,
version: version, version: version,
@ -222,7 +217,7 @@ mod tests {
let expected = KeyFile { let expected = KeyFile {
id: Uuid::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(), id: Uuid::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(),
version: Version::V3, version: Version::V3,
address: "6edddfc6349aff20bc6467ccf276c5b52487f7a8".into(), address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
crypto: Crypto { crypto: Crypto {
cipher: Cipher::Aes128Ctr(Aes128Ctr { cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: "b5a7ec855ec9e2c405371356855fec83".into(), iv: "b5a7ec855ec9e2c405371356855fec83".into(),
@ -273,7 +268,7 @@ mod tests {
let expected = KeyFile { let expected = KeyFile {
id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(), id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
version: Version::V3, version: Version::V3,
address: "6edddfc6349aff20bc6467ccf276c5b52487f7a8".into(), address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
crypto: Crypto { crypto: Crypto {
cipher: Cipher::Aes128Ctr(Aes128Ctr { cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: "b5a7ec855ec9e2c405371356855fec83".into(), iv: "b5a7ec855ec9e2c405371356855fec83".into(),
@ -301,7 +296,7 @@ mod tests {
let file = KeyFile { let file = KeyFile {
id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(), id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
version: Version::V3, version: Version::V3,
address: "6edddfc6349aff20bc6467ccf276c5b52487f7a8".into(), address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
crypto: Crypto { crypto: Crypto {
cipher: Cipher::Aes128Ctr(Aes128Ctr { cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: "b5a7ec855ec9e2c405371356855fec83".into(), iv: "b5a7ec855ec9e2c405371356855fec83".into(),