// Copyright 2015-2017 Parity Technologies (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 .
use std::collections::{BTreeMap, HashMap};
use std::mem;
use std::path::PathBuf;
use parking_lot::{Mutex, RwLock};
use crypto::KEY_ITERATIONS;
use random::Random;
use ethkey::{self, Signature, Address, Message, Secret, Public, KeyPair, ExtendedKeyPair};
use dir::{KeyDirectory, VaultKeyDirectory, VaultKey, SetKeyError};
use account::SafeAccount;
use presale::PresaleWallet;
use json::{self, Uuid, OpaqueKeyFile};
use {import, Error, SimpleSecretStore, SecretStore, SecretVaultRef, StoreAccountRef, Derivation, OpaqueSecret};
/// Accounts store.
pub struct EthStore {
store: EthMultiStore,
}
impl EthStore {
/// Open a new accounts store with given key directory backend.
pub fn open(directory: Box) -> Result {
Self::open_with_iterations(directory, KEY_ITERATIONS as u32)
}
/// Open a new account store with given key directory backend and custom number of iterations.
pub fn open_with_iterations(directory: Box, iterations: u32) -> Result {
Ok(EthStore {
store: EthMultiStore::open_with_iterations(directory, iterations)?,
})
}
fn get(&self, account: &StoreAccountRef) -> Result {
let mut accounts = self.store.get_accounts(account)?.into_iter();
accounts.next().ok_or(Error::InvalidAccount)
}
}
impl SimpleSecretStore for EthStore {
fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &str) -> Result {
self.store.insert_account(vault, secret, password)
}
fn insert_derived(&self, vault: SecretVaultRef, account_ref: &StoreAccountRef, password: &str, derivation: Derivation)
-> Result
{
self.store.insert_derived(vault, account_ref, password, derivation)
}
fn generate_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation) -> Result {
self.store.generate_derived(account_ref, password, derivation)
}
fn account_ref(&self, address: &Address) -> Result {
self.store.account_ref(address)
}
fn accounts(&self) -> Result, Error> {
self.store.accounts()
}
fn change_password(&self, account: &StoreAccountRef, old_password: &str, new_password: &str) -> Result<(), Error> {
self.store.change_password(account, old_password, new_password)
}
fn export_account(&self, account: &StoreAccountRef, password: &str) -> Result {
self.store.export_account(account, password)
}
fn remove_account(&self, account: &StoreAccountRef, password: &str) -> Result<(), Error> {
self.store.remove_account(account, password)
}
fn sign(&self, account: &StoreAccountRef, password: &str, message: &Message) -> Result {
self.get(account)?.sign(password, message)
}
fn sign_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation, message: &Message)
-> Result
{
self.store.sign_derived(account_ref, password, derivation, message)
}
fn agree(&self, account: &StoreAccountRef, password: &str, other: &Public) -> Result {
self.store.agree(account, password, other)
}
fn decrypt(&self, account: &StoreAccountRef, password: &str, shared_mac: &[u8], message: &[u8]) -> Result, Error> {
let account = self.get(account)?;
account.decrypt(password, shared_mac, message)
}
fn create_vault(&self, name: &str, password: &str) -> Result<(), Error> {
self.store.create_vault(name, password)
}
fn open_vault(&self, name: &str, password: &str) -> Result<(), Error> {
self.store.open_vault(name, password)
}
fn close_vault(&self, name: &str) -> Result<(), Error> {
self.store.close_vault(name)
}
fn list_vaults(&self) -> Result, Error> {
self.store.list_vaults()
}
fn list_opened_vaults(&self) -> Result, Error> {
self.store.list_opened_vaults()
}
fn change_vault_password(&self, name: &str, new_password: &str) -> Result<(), Error> {
self.store.change_vault_password(name, new_password)
}
fn change_account_vault(&self, vault: SecretVaultRef, account: StoreAccountRef) -> Result {
self.store.change_account_vault(vault, account)
}
fn get_vault_meta(&self, name: &str) -> Result {
self.store.get_vault_meta(name)
}
fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> {
self.store.set_vault_meta(name, meta)
}
}
impl SecretStore for EthStore {
fn raw_secret(&self, account: &StoreAccountRef, password: &str) -> Result {
Ok(OpaqueSecret(self.get(account)?.crypto.secret(password)?))
}
fn import_presale(&self, vault: SecretVaultRef, json: &[u8], password: &str) -> Result {
let json_wallet = json::PresaleWallet::load(json).map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned()))?;
let wallet = PresaleWallet::from(json_wallet);
let keypair = wallet.decrypt(password).map_err(|_| Error::InvalidPassword)?;
self.insert_account(vault, keypair.secret().clone(), password)
}
fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &str) -> Result {
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 secret = safe_account.crypto.secret(password).map_err(|_| Error::InvalidPassword)?;
safe_account.address = KeyPair::from_secret(secret)?.address();
self.store.import(vault, safe_account)
}
fn test_password(&self, account: &StoreAccountRef, password: &str) -> Result {
let account = self.get(account)?;
Ok(account.check_password(password))
}
fn copy_account(&self, new_store: &SimpleSecretStore, new_vault: SecretVaultRef, account: &StoreAccountRef, password: &str, new_password: &str) -> Result<(), Error> {
let account = self.get(account)?;
let secret = account.crypto.secret(password)?;
new_store.insert_account(new_vault, secret, new_password)?;
Ok(())
}
fn public(&self, account: &StoreAccountRef, password: &str) -> Result {
let account = self.get(account)?;
account.public(password)
}
fn uuid(&self, account: &StoreAccountRef) -> Result {
let account = self.get(account)?;
Ok(account.id.into())
}
fn name(&self, account: &StoreAccountRef) -> Result {
let account = self.get(account)?;
Ok(account.name.clone())
}
fn meta(&self, account: &StoreAccountRef) -> Result {
let account = self.get(account)?;
Ok(account.meta.clone())
}
fn set_name(&self, account_ref: &StoreAccountRef, name: String) -> Result<(), Error> {
let old = self.get(account_ref)?;
let mut safe_account = old.clone();
safe_account.name = name;
// save to file
self.store.update(account_ref, old, safe_account)
}
fn set_meta(&self, account_ref: &StoreAccountRef, meta: String) -> Result<(), Error> {
let old = self.get(account_ref)?;
let mut safe_account = old.clone();
safe_account.meta = meta;
// save to file
self.store.update(account_ref, old, safe_account)
}
fn local_path(&self) -> PathBuf {
self.store.dir.path().cloned().unwrap_or_else(PathBuf::new)
}
fn list_geth_accounts(&self, testnet: bool) -> Vec {
import::read_geth_accounts(testnet)
}
fn import_geth_accounts(&self, vault: SecretVaultRef, desired: Vec, testnet: bool) -> Result, Error> {
let imported_addresses = match vault {
SecretVaultRef::Root => import::import_geth_accounts(&*self.store.dir, desired.into_iter().collect(), testnet),
SecretVaultRef::Vault(vault_name) => {
if let Some(vault) = self.store.vaults.lock().get(&vault_name) {
import::import_geth_accounts(vault.as_key_directory(), desired.into_iter().collect(), testnet)
} else {
Err(Error::VaultNotFound)
}
},
};
imported_addresses
.map(|a| a.into_iter().map(|a| StoreAccountRef::root(a)).collect())
}
}
/// Similar to `EthStore` but may store many accounts (with different passwords) for the same `Address`
pub struct EthMultiStore {
dir: Box,
iterations: u32,
// order lock: cache, then vaults
cache: RwLock>>,
vaults: Mutex>>,
dir_hash: Mutex