Sign with token support
This commit is contained in:
parent
ad440a12bd
commit
6397556cbb
@ -20,8 +20,8 @@ use std::{fs, fmt};
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
use util::{Mutex, RwLock};
|
use util::{Mutex, RwLock, Itertools};
|
||||||
use ethstore::{SecretStore, Error as SSError, SafeAccount, EthStore};
|
use ethstore::{SimpleSecretStore, SecretStore, Error as SSError, SafeAccount, EthStore, EthMultiStore, random_string};
|
||||||
use ethstore::dir::{KeyDirectory};
|
use ethstore::dir::{KeyDirectory};
|
||||||
use ethstore::ethkey::{Address, Message, Public, Secret, Random, Generator};
|
use ethstore::ethkey::{Address, Message, Public, Secret, Random, Generator};
|
||||||
use ethjson::misc::AccountMeta;
|
use ethjson::misc::AccountMeta;
|
||||||
@ -72,21 +72,35 @@ impl From<SSError> for Error {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct NullDir {
|
struct NullDir {
|
||||||
accounts: RwLock<HashMap<Address, SafeAccount>>,
|
accounts: RwLock<HashMap<Address, Vec<SafeAccount>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyDirectory for NullDir {
|
impl KeyDirectory for NullDir {
|
||||||
fn load(&self) -> Result<Vec<SafeAccount>, SSError> {
|
fn load(&self) -> Result<Vec<SafeAccount>, SSError> {
|
||||||
Ok(self.accounts.read().values().cloned().collect())
|
Ok(self.accounts.read().values().cloned().flatten().collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, SSError> {
|
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, SSError> {
|
||||||
self.accounts.write().insert(account.address.clone(), account.clone());
|
self.accounts.write()
|
||||||
|
.entry(account.address.clone())
|
||||||
|
.or_insert_with(Vec::new)
|
||||||
|
.push(account.clone());
|
||||||
Ok(account)
|
Ok(account)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(&self, address: &Address) -> Result<(), SSError> {
|
fn remove(&self, account: &SafeAccount) -> Result<(), SSError> {
|
||||||
self.accounts.write().remove(address);
|
let mut accounts = self.accounts.write();
|
||||||
|
let is_empty = if let Some(mut accounts) = accounts.get_mut(&account.address) {
|
||||||
|
if let Some(position) = accounts.iter().position(|acc| acc == account) {
|
||||||
|
accounts.remove(position);
|
||||||
|
}
|
||||||
|
accounts.is_empty()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
if is_empty {
|
||||||
|
accounts.remove(&account.address);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,10 +177,12 @@ impl AddressBook {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transient_sstore() -> Box<SecretStore> {
|
fn transient_sstore() -> EthMultiStore {
|
||||||
Box::new(EthStore::open(Box::new(NullDir::default())).expect("NullDir load always succeeds; qed")))
|
EthMultiStore::open(Box::new(NullDir::default())).expect("NullDir load always succeeds; qed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccountToken = String;
|
||||||
|
|
||||||
/// Account management.
|
/// Account management.
|
||||||
/// Responsible for unlocking accounts.
|
/// Responsible for unlocking accounts.
|
||||||
pub struct AccountProvider {
|
pub struct AccountProvider {
|
||||||
@ -175,7 +191,7 @@ pub struct AccountProvider {
|
|||||||
/// Accounts on disk
|
/// Accounts on disk
|
||||||
sstore: Box<SecretStore>,
|
sstore: Box<SecretStore>,
|
||||||
/// Accounts unlocked with rolling tokens
|
/// Accounts unlocked with rolling tokens
|
||||||
transient_sstore: Box<SecretStore>,
|
transient_sstore: EthMultiStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AccountProvider {
|
impl AccountProvider {
|
||||||
@ -194,7 +210,7 @@ impl AccountProvider {
|
|||||||
AccountProvider {
|
AccountProvider {
|
||||||
unlocked: Mutex::new(HashMap::new()),
|
unlocked: Mutex::new(HashMap::new()),
|
||||||
address_book: Mutex::new(AddressBook::transient()),
|
address_book: Mutex::new(AddressBook::transient()),
|
||||||
sstore: transient_sstore(),
|
sstore: Box::new(EthStore::open(Box::new(NullDir::default())).expect("NullDir load always succeeds; qed")),
|
||||||
transient_sstore: transient_sstore(),
|
transient_sstore: transient_sstore(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -285,11 +301,8 @@ impl AccountProvider {
|
|||||||
|
|
||||||
/// Returns `true` if the password for `account` is `password`. `false` if not.
|
/// Returns `true` if the password for `account` is `password`. `false` if not.
|
||||||
pub fn test_password(&self, account: &Address, password: &str) -> Result<bool, Error> {
|
pub fn test_password(&self, account: &Address, password: &str) -> Result<bool, Error> {
|
||||||
match self.sstore.sign(account, password, &Default::default()) {
|
self.sstore.test_password(account, password)
|
||||||
Ok(_) => Ok(true),
|
.map_err(Into::into)
|
||||||
Err(SSError::InvalidPassword) => Ok(false),
|
|
||||||
Err(e) => Err(Error::SStore(e)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Permanently removes an account.
|
/// Permanently removes an account.
|
||||||
@ -368,6 +381,26 @@ impl AccountProvider {
|
|||||||
Ok(try!(self.sstore.sign(&account, &password, &message)))
|
Ok(try!(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> {
|
||||||
|
let is_std_password = try!(self.sstore.test_password(&account, &token));
|
||||||
|
|
||||||
|
let new_token = random_string(16);
|
||||||
|
let signature = if is_std_password {
|
||||||
|
// Insert to transient store
|
||||||
|
try!(self.sstore.copy_account(&self.transient_sstore, &account, &token, &new_token));
|
||||||
|
// sign
|
||||||
|
try!(self.sstore.sign(&account, &token, &message))
|
||||||
|
} else {
|
||||||
|
// check transient store
|
||||||
|
try!(self.transient_sstore.change_password(&account, &token, &new_token));
|
||||||
|
// and sign
|
||||||
|
try!(self.transient_sstore.sign(&account, &new_token, &message))
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((signature, new_token))
|
||||||
|
}
|
||||||
|
|
||||||
/// Decrypts a message. If password is not provided the account must be unlocked.
|
/// 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, account: Address, password: Option<String>, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
let password = try!(password.map(Ok).unwrap_or_else(|| self.password(&account)));
|
let password = try!(password.map(Ok).unwrap_or_else(|| self.password(&account)));
|
||||||
@ -450,6 +483,11 @@ mod tests {
|
|||||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let (_signature, token) = ap.sign_with_token(kp.address(), "test", Default::default()).unwrap();
|
let (_signature, token) = ap.sign_with_token(kp.address(), "test".into(), Default::default()).unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
ap.sign_with_token(kp.address(), token.clone(), Default::default())
|
||||||
|
.expect("First usage of token should be correct.");
|
||||||
|
assert!(ap.sign_with_token(kp.address(), token, Default::default()).is_err(), "Second usage of the same token should fail.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ use random::Random;
|
|||||||
use ethkey::{Signature, Address, Message, Secret, Public};
|
use ethkey::{Signature, Address, Message, Secret, Public};
|
||||||
use dir::KeyDirectory;
|
use dir::KeyDirectory;
|
||||||
use account::SafeAccount;
|
use account::SafeAccount;
|
||||||
use {Error, SecretStore};
|
use {Error, SimpleSecretStore, SecretStore};
|
||||||
use json;
|
use json;
|
||||||
use json::UUID;
|
use json::UUID;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
@ -30,9 +30,7 @@ use presale::PresaleWallet;
|
|||||||
use import;
|
use import;
|
||||||
|
|
||||||
pub struct EthStore {
|
pub struct EthStore {
|
||||||
dir: Box<KeyDirectory>,
|
store: EthMultiStore,
|
||||||
iterations: u32,
|
|
||||||
cache: RwLock<BTreeMap<Address, SafeAccount>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EthStore {
|
impl EthStore {
|
||||||
@ -41,57 +39,46 @@ impl EthStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_with_iterations(directory: Box<KeyDirectory>, iterations: u32) -> Result<Self, Error> {
|
pub fn open_with_iterations(directory: Box<KeyDirectory>, iterations: u32) -> Result<Self, Error> {
|
||||||
let accounts = try!(directory.load());
|
Ok(EthStore {
|
||||||
let cache = accounts.into_iter().map(|account| (account.address.clone(), account)).collect();
|
store: try!(EthMultiStore::open_with_iterations(directory, iterations)),
|
||||||
let store = EthStore {
|
})
|
||||||
dir: directory,
|
|
||||||
iterations: iterations,
|
|
||||||
cache: RwLock::new(cache),
|
|
||||||
};
|
|
||||||
Ok(store)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn save(&self, account: SafeAccount) -> Result<(), Error> {
|
|
||||||
// save to file
|
|
||||||
let account = try!(self.dir.insert(account));
|
|
||||||
|
|
||||||
// update cache
|
|
||||||
let mut cache = self.cache.write();
|
|
||||||
cache.insert(account.address.clone(), account);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reload_accounts(&self) -> Result<(), Error> {
|
|
||||||
let mut cache = self.cache.write();
|
|
||||||
let accounts = try!(self.dir.load());
|
|
||||||
let new_accounts: BTreeMap<_, _> = accounts.into_iter().map(|account| (account.address.clone(), account)).collect();
|
|
||||||
mem::replace(&mut *cache, new_accounts);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, address: &Address) -> Result<SafeAccount, Error> {
|
fn get(&self, address: &Address) -> Result<SafeAccount, Error> {
|
||||||
{
|
let mut accounts = try!(self.store.get(address)).into_iter();
|
||||||
let cache = self.cache.read();
|
accounts.next().ok_or(Error::InvalidAccount)
|
||||||
if let Some(account) = cache.get(address) {
|
|
||||||
return Ok(account.clone())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try!(self.reload_accounts());
|
|
||||||
let cache = self.cache.read();
|
impl SimpleSecretStore for EthStore {
|
||||||
cache.get(address).cloned().ok_or(Error::InvalidAccount)
|
fn insert_account(&self, secret: Secret, password: &str) -> Result<Address, Error> {
|
||||||
|
self.store.insert_account(secret, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accounts(&self) -> Result<Vec<Address>, Error> {
|
||||||
|
self.store.accounts()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_password(&self, address: &Address, old_password: &str, new_password: &str) -> Result<(), Error> {
|
||||||
|
self.store.change_password(address, old_password, new_password)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_account(&self, address: &Address, password: &str) -> Result<(), Error> {
|
||||||
|
self.store.remove_account(address, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sign(&self, address: &Address, password: &str, message: &Message) -> Result<Signature, Error> {
|
||||||
|
let account = try!(self.get(address));
|
||||||
|
account.sign(password, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrypt(&self, account: &Address, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
|
let account = try!(self.get(account));
|
||||||
|
account.decrypt(password, shared_mac, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SecretStore for EthStore {
|
impl SecretStore for EthStore {
|
||||||
fn insert_account(&self, secret: Secret, password: &str) -> Result<Address, Error> {
|
|
||||||
let keypair = try!(KeyPair::from_secret(secret).map_err(|_| Error::CreationFailed));
|
|
||||||
let id: [u8; 16] = Random::random();
|
|
||||||
let account = SafeAccount::create(&keypair, id, password, self.iterations, "".to_owned(), "{}".to_owned());
|
|
||||||
let address = account.address.clone();
|
|
||||||
try!(self.save(account));
|
|
||||||
Ok(address)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn import_presale(&self, json: &[u8], password: &str) -> Result<Address, Error> {
|
fn import_presale(&self, json: &[u8], password: &str) -> Result<Address, Error> {
|
||||||
let json_wallet = try!(json::PresaleWallet::load(json).map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned())));
|
let json_wallet = try!(json::PresaleWallet::load(json).map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned())));
|
||||||
let wallet = PresaleWallet::from(json_wallet);
|
let wallet = PresaleWallet::from(json_wallet);
|
||||||
@ -105,46 +92,20 @@ impl SecretStore for EthStore {
|
|||||||
let secret = try!(safe_account.crypto.secret(password).map_err(|_| Error::InvalidPassword));
|
let secret = try!(safe_account.crypto.secret(password).map_err(|_| Error::InvalidPassword));
|
||||||
safe_account.address = try!(KeyPair::from_secret(secret)).address();
|
safe_account.address = try!(KeyPair::from_secret(secret)).address();
|
||||||
let address = safe_account.address.clone();
|
let address = safe_account.address.clone();
|
||||||
try!(self.save(safe_account));
|
try!(self.store.save(safe_account));
|
||||||
Ok(address)
|
Ok(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accounts(&self) -> Result<Vec<Address>, Error> {
|
fn test_password(&self, address: &Address, password: &str) -> Result<bool, Error> {
|
||||||
try!(self.reload_accounts());
|
let account = try!(self.get(address));
|
||||||
Ok(self.cache.read().keys().cloned().collect())
|
Ok(account.check_password(password))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_password(&self, address: &Address, old_password: &str, new_password: &str) -> Result<(), Error> {
|
fn copy_account(&self, new_store: &SimpleSecretStore, address: &Address, password: &str, new_password: &str) -> Result<(), Error> {
|
||||||
// change password
|
|
||||||
let account = try!(self.get(address));
|
let account = try!(self.get(address));
|
||||||
let account = try!(account.change_password(old_password, new_password, self.iterations));
|
let secret = try!(account.crypto.secret(password));
|
||||||
|
try!(new_store.insert_account(secret, new_password));
|
||||||
// save to file
|
|
||||||
self.save(account)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_account(&self, address: &Address, password: &str) -> Result<(), Error> {
|
|
||||||
let account = try!(self.get(address));
|
|
||||||
let can_remove = account.check_password(password);
|
|
||||||
|
|
||||||
if can_remove {
|
|
||||||
try!(self.dir.remove(&account));
|
|
||||||
let mut cache = self.cache.write();
|
|
||||||
cache.remove(address);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
|
||||||
Err(Error::InvalidPassword)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sign(&self, address: &Address, password: &str, message: &Message) -> Result<Signature, Error> {
|
|
||||||
let account = try!(self.get(address));
|
|
||||||
account.sign(password, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decrypt(&self, account: &Address, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
|
||||||
let account = try!(self.get(account));
|
|
||||||
account.decrypt(password, shared_mac, message)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn public(&self, account: &Address, password: &str) -> Result<Public, Error> {
|
fn public(&self, account: &Address, password: &str) -> Result<Public, Error> {
|
||||||
@ -172,7 +133,7 @@ impl SecretStore for EthStore {
|
|||||||
account.name = name;
|
account.name = name;
|
||||||
|
|
||||||
// save to file
|
// save to file
|
||||||
self.save(account)
|
self.store.save(account)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_meta(&self, address: &Address, meta: String) -> Result<(), Error> {
|
fn set_meta(&self, address: &Address, meta: String) -> Result<(), Error> {
|
||||||
@ -180,11 +141,11 @@ impl SecretStore for EthStore {
|
|||||||
account.meta = meta;
|
account.meta = meta;
|
||||||
|
|
||||||
// save to file
|
// save to file
|
||||||
self.save(account)
|
self.store.save(account)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn local_path(&self) -> String {
|
fn local_path(&self) -> String {
|
||||||
self.dir.path().map(|p| p.to_string_lossy().into_owned()).unwrap_or_else(|| String::new())
|
self.store.dir.path().map(|p| p.to_string_lossy().into_owned()).unwrap_or_else(|| String::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_geth_accounts(&self, testnet: bool) -> Vec<Address> {
|
fn list_geth_accounts(&self, testnet: bool) -> Vec<Address> {
|
||||||
@ -192,7 +153,7 @@ impl SecretStore for EthStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn import_geth_accounts(&self, desired: Vec<Address>, testnet: bool) -> Result<Vec<Address>, Error> {
|
fn import_geth_accounts(&self, desired: Vec<Address>, testnet: bool) -> Result<Vec<Address>, Error> {
|
||||||
import::import_geth_accounts(&*self.dir, desired.into_iter().collect(), testnet)
|
import::import_geth_accounts(&*self.store.dir, desired.into_iter().collect(), testnet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +171,7 @@ impl EthMultiStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_with_iterations(directory: Box<KeyDirectory>, iterations: u32) -> Result<Self, Error> {
|
pub fn open_with_iterations(directory: Box<KeyDirectory>, iterations: u32) -> Result<Self, Error> {
|
||||||
let mut store = EthMultiStore {
|
let store = EthMultiStore {
|
||||||
dir: directory,
|
dir: directory,
|
||||||
iterations: iterations,
|
iterations: iterations,
|
||||||
cache: Default::default(),
|
cache: Default::default(),
|
||||||
@ -252,7 +213,7 @@ impl EthMultiStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_account(&self, account: SafeAccount) -> Result<(), Error> {
|
fn save(&self, account: SafeAccount) -> Result<(), Error> {
|
||||||
//save to file
|
//save to file
|
||||||
let account = try!(self.dir.insert(account));
|
let account = try!(self.dir.insert(account));
|
||||||
|
|
||||||
@ -263,7 +224,24 @@ impl EthMultiStore {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_account(&self, address: &Address, password: &str) -> Result<(), Error> {
|
}
|
||||||
|
|
||||||
|
impl SimpleSecretStore for EthMultiStore {
|
||||||
|
fn insert_account(&self, secret: Secret, password: &str) -> Result<Address, Error> {
|
||||||
|
let keypair = try!(KeyPair::from_secret(secret).map_err(|_| Error::CreationFailed));
|
||||||
|
let id: [u8; 16] = Random::random();
|
||||||
|
let account = SafeAccount::create(&keypair, id, password, self.iterations, "".to_owned(), "{}".to_owned());
|
||||||
|
let address = account.address.clone();
|
||||||
|
try!(self.save(account));
|
||||||
|
Ok(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accounts(&self) -> Result<Vec<Address>, Error> {
|
||||||
|
try!(self.reload_accounts());
|
||||||
|
Ok(self.cache.read().keys().cloned().collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_account(&self, address: &Address, password: &str) -> Result<(), Error> {
|
||||||
let accounts = try!(self.get(address));
|
let accounts = try!(self.get(address));
|
||||||
|
|
||||||
for account in accounts {
|
for account in accounts {
|
||||||
@ -294,19 +272,19 @@ impl EthMultiStore {
|
|||||||
Err(Error::InvalidPassword)
|
Err(Error::InvalidPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_password(&self, address: &Address, old_password: &str, new_password: &str) -> Result<(), Error> {
|
fn change_password(&self, address: &Address, old_password: &str, new_password: &str) -> Result<(), Error> {
|
||||||
let accounts = try!(self.get(address));
|
let accounts = try!(self.get(address));
|
||||||
for account in accounts {
|
for account in accounts {
|
||||||
// First remove
|
// First remove
|
||||||
try!(self.remove_account(&address, old_password));
|
try!(self.remove_account(&address, old_password));
|
||||||
// Then insert back with new password
|
// Then insert back with new password
|
||||||
let new_account = try!(account.change_password(old_password, new_password, self.iterations));
|
let new_account = try!(account.change_password(old_password, new_password, self.iterations));
|
||||||
try!(self.insert_account(new_account));
|
try!(self.save(new_account));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sign(&self, address: &Address, password: &str, message: &Message) -> Result<Signature, Error> {
|
fn sign(&self, address: &Address, password: &str, message: &Message) -> Result<Signature, Error> {
|
||||||
let accounts = try!(self.get(address));
|
let accounts = try!(self.get(address));
|
||||||
for account in accounts {
|
for account in accounts {
|
||||||
if account.check_password(password) {
|
if account.check_password(password) {
|
||||||
@ -317,7 +295,7 @@ impl EthMultiStore {
|
|||||||
Err(Error::InvalidPassword)
|
Err(Error::InvalidPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decrypt(&self, account: &Address, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
fn decrypt(&self, account: &Address, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
let accounts = try!(self.get(account));
|
let accounts = try!(self.get(account));
|
||||||
for account in accounts {
|
for account in accounts {
|
||||||
if account.check_password(password) {
|
if account.check_password(password) {
|
||||||
@ -328,3 +306,9 @@ impl EthMultiStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
fn should_have_some_tests() {
|
||||||
|
assert_eq!(true, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -50,8 +50,8 @@ mod secret_store;
|
|||||||
|
|
||||||
pub use self::account::SafeAccount;
|
pub use self::account::SafeAccount;
|
||||||
pub use self::error::Error;
|
pub use self::error::Error;
|
||||||
pub use self::ethstore::EthStore;
|
pub use self::ethstore::{EthStore, EthMultiStore};
|
||||||
pub use self::import::{import_accounts, read_geth_accounts};
|
pub use self::import::{import_accounts, read_geth_accounts};
|
||||||
pub use self::presale::PresaleWallet;
|
pub use self::presale::PresaleWallet;
|
||||||
pub use self::secret_store::SecretStore;
|
pub use self::secret_store::{SimpleSecretStore, SecretStore};
|
||||||
pub use self::random::random_phrase;
|
pub use self::random::{random_phrase, random_string};
|
||||||
|
@ -51,10 +51,16 @@ pub fn random_phrase(words: usize) -> String {
|
|||||||
.map(|s| s.to_owned())
|
.map(|s| s.to_owned())
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
let mut rng = OsRng::new().unwrap();
|
let mut rng = OsRng::new().expect("Not able to operate without random source.");
|
||||||
(0..words).map(|_| rng.choose(&WORDS).unwrap()).join(" ")
|
(0..words).map(|_| rng.choose(&WORDS).unwrap()).join(" ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate a random string of given length.
|
||||||
|
pub fn random_string(length: usize) -> String {
|
||||||
|
let mut rng = OsRng::new().expect("Not able to operate without random source.");
|
||||||
|
rng.gen_ascii_chars().take(length).collect()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::random_phrase;
|
use super::random_phrase;
|
||||||
|
@ -18,18 +18,25 @@ use ethkey::{Address, Message, Signature, Secret, Public};
|
|||||||
use Error;
|
use Error;
|
||||||
use json::UUID;
|
use json::UUID;
|
||||||
|
|
||||||
pub trait SecretStore: Send + Sync {
|
pub trait SimpleSecretStore: Send + Sync {
|
||||||
fn insert_account(&self, secret: Secret, password: &str) -> Result<Address, Error>;
|
fn insert_account(&self, secret: Secret, password: &str) -> Result<Address, Error>;
|
||||||
fn import_presale(&self, json: &[u8], password: &str) -> Result<Address, Error>;
|
|
||||||
fn import_wallet(&self, json: &[u8], password: &str) -> Result<Address, Error>;
|
|
||||||
fn change_password(&self, account: &Address, old_password: &str, new_password: &str) -> Result<(), Error>;
|
fn change_password(&self, account: &Address, old_password: &str, new_password: &str) -> Result<(), Error>;
|
||||||
fn remove_account(&self, account: &Address, password: &str) -> Result<(), Error>;
|
fn remove_account(&self, account: &Address, password: &str) -> Result<(), Error>;
|
||||||
|
|
||||||
fn sign(&self, account: &Address, password: &str, message: &Message) -> Result<Signature, Error>;
|
fn sign(&self, account: &Address, password: &str, message: &Message) -> Result<Signature, Error>;
|
||||||
fn decrypt(&self, account: &Address, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error>;
|
fn decrypt(&self, account: &Address, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error>;
|
||||||
fn public(&self, account: &Address, password: &str) -> Result<Public, Error>;
|
|
||||||
|
|
||||||
fn accounts(&self) -> Result<Vec<Address>, Error>;
|
fn accounts(&self) -> Result<Vec<Address>, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SecretStore: SimpleSecretStore {
|
||||||
|
fn import_presale(&self, json: &[u8], password: &str) -> Result<Address, Error>;
|
||||||
|
fn import_wallet(&self, json: &[u8], password: &str) -> Result<Address, Error>;
|
||||||
|
fn copy_account(&self, new_store: &SimpleSecretStore, account: &Address, password: &str, new_password: &str) -> Result<(), Error>;
|
||||||
|
fn test_password(&self, account: &Address, password: &str) -> Result<bool, Error>;
|
||||||
|
|
||||||
|
fn public(&self, account: &Address, password: &str) -> Result<Public, Error>;
|
||||||
|
|
||||||
fn uuid(&self, account: &Address) -> Result<UUID, Error>;
|
fn uuid(&self, account: &Address) -> Result<UUID, Error>;
|
||||||
fn name(&self, account: &Address) -> Result<String, Error>;
|
fn name(&self, account: &Address) -> Result<String, Error>;
|
||||||
fn meta(&self, account: &Address) -> Result<String, Error>;
|
fn meta(&self, account: &Address) -> Result<String, Error>;
|
||||||
|
Loading…
Reference in New Issue
Block a user