Initial commit for vaults (#4312)
* initial commit for vaults * fixed TODO * public docs * vault_file.json now contains enc(pwd hash) * removed doc
This commit is contained in:
committed by
Nikolay Volf
parent
bf1e7ecfcb
commit
9ac4d83ca3
79
ethcore/src/account_provider/mod.rs
Normal file → Executable file
79
ethcore/src/account_provider/mod.rs
Normal file → Executable file
@@ -24,7 +24,8 @@ use std::fmt;
|
||||
use std::collections::HashMap;
|
||||
use std::time::{Instant, Duration};
|
||||
use util::RwLock;
|
||||
use ethstore::{SimpleSecretStore, SecretStore, Error as SSError, EthStore, EthMultiStore, random_string};
|
||||
use ethstore::{SimpleSecretStore, SecretStore, Error as SSError, EthStore, EthMultiStore,
|
||||
random_string, SecretVaultRef, StoreAccountRef};
|
||||
use ethstore::dir::MemoryDirectory;
|
||||
use ethstore::ethkey::{Address, Message, Public, Secret, Random, Generator};
|
||||
use ethjson::misc::AccountMeta;
|
||||
@@ -96,7 +97,7 @@ type AccountToken = String;
|
||||
/// Account management.
|
||||
/// Responsible for unlocking accounts.
|
||||
pub struct AccountProvider {
|
||||
unlocked: RwLock<HashMap<Address, AccountData>>,
|
||||
unlocked: RwLock<HashMap<StoreAccountRef, AccountData>>,
|
||||
address_book: RwLock<AddressBook>,
|
||||
dapps_settings: RwLock<DappsSettingsStore>,
|
||||
/// Accounts on disk
|
||||
@@ -138,27 +139,27 @@ impl AccountProvider {
|
||||
let acc = Random.generate().expect("secp context has generation capabilities; qed");
|
||||
let public = acc.public().clone();
|
||||
let secret = acc.secret().clone();
|
||||
let address = self.sstore.insert_account(secret, password)?;
|
||||
Ok((address, public))
|
||||
let account = self.sstore.insert_account(SecretVaultRef::Root, secret, password)?;
|
||||
Ok((account.address, public))
|
||||
}
|
||||
|
||||
/// Inserts new account into underlying store.
|
||||
/// Does not unlock account!
|
||||
pub fn insert_account(&self, secret: Secret, password: &str) -> Result<Address, Error> {
|
||||
let address = self.sstore.insert_account(secret, password)?;
|
||||
Ok(address)
|
||||
let account = self.sstore.insert_account(SecretVaultRef::Root, secret, password)?;
|
||||
Ok(account.address)
|
||||
}
|
||||
|
||||
/// Import a new presale wallet.
|
||||
pub fn import_presale(&self, presale_json: &[u8], password: &str) -> Result<Address, Error> {
|
||||
let address = self.sstore.import_presale(presale_json, password)?;
|
||||
Ok(Address::from(address).into())
|
||||
let account = self.sstore.import_presale(SecretVaultRef::Root, presale_json, password)?;
|
||||
Ok(Address::from(account.address).into())
|
||||
}
|
||||
|
||||
/// Import a new presale wallet.
|
||||
pub fn import_wallet(&self, json: &[u8], password: &str) -> Result<Address, Error> {
|
||||
let address = self.sstore.import_wallet(json, password)?;
|
||||
Ok(Address::from(address).into())
|
||||
let account = self.sstore.import_wallet(SecretVaultRef::Root, json, password)?;
|
||||
Ok(Address::from(account.address).into())
|
||||
}
|
||||
|
||||
/// Checks whether an account with a given address is present.
|
||||
@@ -169,7 +170,7 @@ impl AccountProvider {
|
||||
/// Returns addresses of all accounts.
|
||||
pub fn accounts(&self) -> Result<Vec<Address>, Error> {
|
||||
let accounts = self.sstore.accounts()?;
|
||||
Ok(accounts)
|
||||
Ok(accounts.into_iter().map(|a| a.address).collect())
|
||||
}
|
||||
|
||||
/// Sets a whitelist of accounts exposed for unknown dapps.
|
||||
@@ -247,13 +248,14 @@ impl AccountProvider {
|
||||
pub fn accounts_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> {
|
||||
let r: HashMap<Address, AccountMeta> = self.sstore.accounts()?
|
||||
.into_iter()
|
||||
.map(|a| (a.clone(), self.account_meta(a).ok().unwrap_or_default()))
|
||||
.map(|a| (a.address.clone(), self.account_meta(a.address).ok().unwrap_or_default()))
|
||||
.collect();
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn account_meta(&self, account: Address) -> Result<AccountMeta, Error> {
|
||||
pub fn account_meta(&self, address: Address) -> Result<AccountMeta, Error> {
|
||||
let account = StoreAccountRef::root(address);
|
||||
Ok(AccountMeta {
|
||||
name: self.sstore.name(&account)?,
|
||||
meta: self.sstore.meta(&account)?,
|
||||
@@ -262,38 +264,39 @@ impl AccountProvider {
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn set_account_name(&self, account: Address, name: String) -> Result<(), Error> {
|
||||
self.sstore.set_name(&account, name)?;
|
||||
pub fn set_account_name(&self, address: Address, name: String) -> Result<(), Error> {
|
||||
self.sstore.set_name(&StoreAccountRef::root(address), name)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn set_account_meta(&self, account: Address, meta: String) -> Result<(), Error> {
|
||||
self.sstore.set_meta(&account, meta)?;
|
||||
pub fn set_account_meta(&self, address: Address, meta: String) -> Result<(), Error> {
|
||||
self.sstore.set_meta(&StoreAccountRef::root(address), meta)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns `true` if the password for `account` is `password`. `false` if not.
|
||||
pub fn test_password(&self, account: &Address, password: &str) -> Result<bool, Error> {
|
||||
self.sstore.test_password(account, password)
|
||||
pub fn test_password(&self, address: &Address, password: &str) -> Result<bool, Error> {
|
||||
self.sstore.test_password(&StoreAccountRef::root(address.clone()), password)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Permanently removes an account.
|
||||
pub fn kill_account(&self, account: &Address, password: &str) -> Result<(), Error> {
|
||||
self.sstore.remove_account(account, &password)?;
|
||||
pub fn kill_account(&self, address: &Address, password: &str) -> Result<(), Error> {
|
||||
self.sstore.remove_account(&StoreAccountRef::root(address.clone()), &password)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given.
|
||||
pub fn change_password(&self, account: &Address, password: String, new_password: String) -> Result<(), Error> {
|
||||
self.sstore.change_password(account, &password, &new_password).map_err(Error::SStore)
|
||||
pub fn change_password(&self, address: &Address, password: String, new_password: String) -> Result<(), Error> {
|
||||
self.sstore.change_password(&StoreAccountRef::root(address.clone()), &password, &new_password).map_err(Error::SStore)
|
||||
}
|
||||
|
||||
/// Helper method used for unlocking accounts.
|
||||
fn unlock_account(&self, account: Address, password: String, unlock: Unlock) -> Result<(), Error> {
|
||||
fn unlock_account(&self, address: Address, password: String, unlock: Unlock) -> Result<(), Error> {
|
||||
// verify password by signing dump message
|
||||
// result may be discarded
|
||||
let account = StoreAccountRef::root(address);
|
||||
let _ = self.sstore.sign(&account, &password, &Default::default())?;
|
||||
|
||||
// check if account is already unlocked pernamently, if it is, do nothing
|
||||
@@ -313,7 +316,7 @@ impl AccountProvider {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn password(&self, account: &Address) -> Result<String, Error> {
|
||||
fn password(&self, account: &StoreAccountRef) -> Result<String, Error> {
|
||||
let mut unlocked = self.unlocked.write();
|
||||
let data = unlocked.get(account).ok_or(Error::NotUnlocked)?.clone();
|
||||
if let Unlock::Temp = data.unlock {
|
||||
@@ -344,25 +347,28 @@ impl AccountProvider {
|
||||
}
|
||||
|
||||
/// Checks if given account is unlocked
|
||||
pub fn is_unlocked(&self, account: Address) -> bool {
|
||||
pub fn is_unlocked(&self, address: Address) -> bool {
|
||||
let unlocked = self.unlocked.read();
|
||||
let account = StoreAccountRef::root(address);
|
||||
unlocked.get(&account).is_some()
|
||||
}
|
||||
|
||||
/// Signs the message. If password is not provided the account must be unlocked.
|
||||
pub fn sign(&self, account: Address, password: Option<String>, message: Message) -> Result<Signature, Error> {
|
||||
pub fn sign(&self, address: Address, password: Option<String>, message: Message) -> Result<Signature, Error> {
|
||||
let account = StoreAccountRef::root(address);
|
||||
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
|
||||
Ok(self.sstore.sign(&account, &password, &message)?)
|
||||
}
|
||||
|
||||
/// Signs given message with supplied token. Returns a token to use in next signing within this session.
|
||||
pub fn sign_with_token(&self, account: Address, token: AccountToken, message: Message) -> Result<(Signature, AccountToken), Error> {
|
||||
pub fn sign_with_token(&self, address: Address, token: AccountToken, message: Message) -> Result<(Signature, AccountToken), Error> {
|
||||
let account = StoreAccountRef::root(address);
|
||||
let is_std_password = self.sstore.test_password(&account, &token)?;
|
||||
|
||||
let new_token = random_string(16);
|
||||
let signature = if is_std_password {
|
||||
// Insert to transient store
|
||||
self.sstore.copy_account(&self.transient_sstore, &account, &token, &new_token)?;
|
||||
self.sstore.copy_account(&self.transient_sstore, SecretVaultRef::Root, &account, &token, &new_token)?;
|
||||
// sign
|
||||
self.sstore.sign(&account, &token, &message)?
|
||||
} else {
|
||||
@@ -376,15 +382,16 @@ impl AccountProvider {
|
||||
}
|
||||
|
||||
/// Decrypts a message with given token. Returns a token to use in next operation for this account.
|
||||
pub fn decrypt_with_token(&self, account: Address, token: AccountToken, shared_mac: &[u8], message: &[u8])
|
||||
pub fn decrypt_with_token(&self, address: Address, token: AccountToken, shared_mac: &[u8], message: &[u8])
|
||||
-> Result<(Vec<u8>, AccountToken), Error>
|
||||
{
|
||||
let account = StoreAccountRef::root(address);
|
||||
let is_std_password = self.sstore.test_password(&account, &token)?;
|
||||
|
||||
let new_token = random_string(16);
|
||||
let message = if is_std_password {
|
||||
// Insert to transient store
|
||||
self.sstore.copy_account(&self.transient_sstore, &account, &token, &new_token)?;
|
||||
self.sstore.copy_account(&self.transient_sstore, SecretVaultRef::Root, &account, &token, &new_token)?;
|
||||
// decrypt
|
||||
self.sstore.decrypt(&account, &token, shared_mac, message)?
|
||||
} else {
|
||||
@@ -398,7 +405,8 @@ impl AccountProvider {
|
||||
}
|
||||
|
||||
/// Decrypts a message. If password is not provided the account must be unlocked.
|
||||
pub fn decrypt(&self, account: Address, password: Option<String>, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
pub fn decrypt(&self, address: Address, password: Option<String>, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let account = StoreAccountRef::root(address);
|
||||
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
|
||||
Ok(self.sstore.decrypt(&account, &password, shared_mac, message)?)
|
||||
}
|
||||
@@ -410,7 +418,9 @@ impl AccountProvider {
|
||||
|
||||
/// Returns the underlying `SecretStore` reference if one exists.
|
||||
pub fn import_geth_accounts(&self, desired: Vec<Address>, testnet: bool) -> Result<Vec<Address>, Error> {
|
||||
self.sstore.import_geth_accounts(desired, testnet).map_err(Into::into)
|
||||
self.sstore.import_geth_accounts(SecretVaultRef::Root, desired, testnet)
|
||||
.map(|a| a.into_iter().map(|a| a.address).collect())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,6 +429,7 @@ mod tests {
|
||||
use super::{AccountProvider, Unlock, DappId};
|
||||
use std::time::Instant;
|
||||
use ethstore::ethkey::{Generator, Random};
|
||||
use ethstore::StoreAccountRef;
|
||||
|
||||
#[test]
|
||||
fn unlock_account_temp() {
|
||||
@@ -453,7 +464,7 @@ mod tests {
|
||||
assert!(ap.unlock_account_timed(kp.address(), "test1".into(), 60000).is_err());
|
||||
assert!(ap.unlock_account_timed(kp.address(), "test".into(), 60000).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
ap.unlocked.write().get_mut(&kp.address()).unwrap().unlock = Unlock::Timed(Instant::now());
|
||||
ap.unlocked.write().get_mut(&StoreAccountRef::root(kp.address())).unwrap().unlock = Unlock::Timed(Instant::now());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user