Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Commit

Permalink
disable hardware-wallets that don't support libusb
Browse files Browse the repository at this point in the history
  • Loading branch information
niklasad1 committed Apr 23, 2018
1 parent 7a28f72 commit d166498
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 13 deletions.
69 changes: 63 additions & 6 deletions ethcore/src/account_provider/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,15 @@ use ethstore::{
use ethstore::accounts_dir::MemoryDirectory;
use ethstore::ethkey::{Address, Message, Public, Secret, Random, Generator};
use ethjson::misc::AccountMeta;
use hardware_wallet::{Error as HardwareError, HardwareWalletManager, KeyPath, TransactionInfo};
use super::transaction::{Action, Transaction};

pub use ethstore::ethkey::Signature;
pub use ethstore::{Derivation, IndexDerivation, KeyFile};

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
use super::transaction::{Action, Transaction};
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
use hardware_wallet::{Error as HardwareError, HardwareWalletManager, KeyPath, TransactionInfo};

/// Type of unlock.
#[derive(Clone, PartialEq)]
enum Unlock {
Expand Down Expand Up @@ -63,6 +67,7 @@ pub enum SignError {
/// Account does not exist.
NotFound,
/// Low-level hardware device error.
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
Hardware(HardwareError),
/// Low-level error from store
SStore(SSError),
Expand All @@ -73,12 +78,14 @@ impl fmt::Display for SignError {
match *self {
SignError::NotUnlocked => write!(f, "Account is locked"),
SignError::NotFound => write!(f, "Account does not exist"),
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
SignError::Hardware(ref e) => write!(f, "{}", e),
SignError::SStore(ref e) => write!(f, "{}", e),
}
}
}

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
impl From<HardwareError> for SignError {
fn from(e: HardwareError) -> Self {
SignError::Hardware(e)
Expand Down Expand Up @@ -130,6 +137,7 @@ pub struct AccountProvider {
/// Accounts unlocked with rolling tokens
transient_sstore: EthMultiStore,
/// Accounts in hardware wallets.
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
hardware_store: Option<HardwareWalletManager>,
/// When unlocking account permanently we additionally keep a raw secret in memory
/// to increase the performance of transaction signing.
Expand Down Expand Up @@ -163,8 +171,10 @@ impl Default for AccountProviderSettings {

impl AccountProvider {
/// Creates new account provider.
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
pub fn new(sstore: Box<SecretStore>, settings: AccountProviderSettings) -> Self {
let mut hardware_store = None;

if settings.enable_hardware_wallets {
match HardwareWalletManager::new() {
Ok(manager) => {
Expand Down Expand Up @@ -201,6 +211,34 @@ impl AccountProvider {
}
}

/// Creates new account provider without a hardware-wallet
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android")))]
pub fn new(sstore: Box<SecretStore>, settings: AccountProviderSettings) -> Self {
if let Ok(accounts) = sstore.accounts() {
for account in accounts.into_iter().filter(|a| settings.blacklisted_accounts.contains(&a.address)) {
warn!("Local Account {} has a blacklisted (known to be weak) address and will be ignored",
account.address);
}
}

// Remove blacklisted accounts from address book.
let mut address_book = AddressBook::new(&sstore.local_path());
for addr in &settings.blacklisted_accounts {
address_book.remove(*addr);
}

AccountProvider {
unlocked_secrets: RwLock::new(HashMap::new()),
unlocked: RwLock::new(HashMap::new()),
address_book: RwLock::new(address_book),
dapps_settings: RwLock::new(DappsSettingsStore::new(&sstore.local_path())),
sstore: sstore,
transient_sstore: transient_sstore(),
unlock_keep_secret: settings.unlock_keep_secret,
blacklisted_accounts: settings.blacklisted_accounts,
}
}

/// Creates not disk backed provider.
pub fn transient_provider() -> Self {
AccountProvider {
Expand All @@ -210,6 +248,7 @@ impl AccountProvider {
dapps_settings: RwLock::new(DappsSettingsStore::transient()),
sstore: Box::new(EthStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed")),
transient_sstore: transient_sstore(),
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
hardware_store: None,
unlock_keep_secret: false,
blacklisted_accounts: vec![],
Expand Down Expand Up @@ -280,20 +319,22 @@ impl AccountProvider {
pub fn accounts(&self) -> Result<Vec<Address>, Error> {
let accounts = self.sstore.accounts()?;
Ok(accounts
.into_iter()
.map(|a| a.address)
.filter(|address| !self.blacklisted_accounts.contains(address))
.collect()
.into_iter()
.map(|a| a.address)
.filter(|address| !self.blacklisted_accounts.contains(address))
.collect()
)
}

/// Returns addresses of hardware accounts.
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
pub fn hardware_accounts(&self) -> Result<Vec<Address>, Error> {
let accounts = self.hardware_store.as_ref().map_or(Vec::new(), |h| h.list_wallets());
Ok(accounts.into_iter().map(|a| a.address).collect())
}

/// Get a list of paths to locked hardware wallets
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
pub fn locked_hardware_accounts(&self) -> Result<Vec<String>, SignError> {
match self.hardware_store.as_ref().map(|h| h.list_locked_wallets()) {
None => Err(SignError::NotFound),
Expand All @@ -303,6 +344,7 @@ impl AccountProvider {
}

/// Provide a pin to a locked hardware wallet on USB path to unlock it
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
pub fn hardware_pin_matrix_ack(&self, path: &str, pin: &str) -> Result<bool, SignError> {
match self.hardware_store.as_ref().map(|h| h.pin_matrix_ack(path, pin)) {
None => Err(SignError::NotFound),
Expand Down Expand Up @@ -511,6 +553,7 @@ impl AccountProvider {
}

/// Returns each hardware account along with name and meta.
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
pub fn hardware_accounts_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> {
let r = self.hardware_accounts()?
.into_iter()
Expand All @@ -520,11 +563,13 @@ impl AccountProvider {
}

/// Returns each hardware account along with name and meta.
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
pub fn is_hardware_address(&self, address: &Address) -> bool {
self.hardware_store.as_ref().and_then(|s| s.wallet_info(address)).is_some()
}

/// Returns each account along with name and meta.
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
pub fn account_meta(&self, address: Address) -> Result<AccountMeta, Error> {
if let Some(info) = self.hardware_store.as_ref().and_then(|s| s.wallet_info(&address)) {
Ok(AccountMeta {
Expand All @@ -542,6 +587,17 @@ impl AccountProvider {
}
}

/// Returns each account along with name and meta without a hardware-wallet
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android")))]
pub fn account_meta(&self, address: Address) -> Result<AccountMeta, Error> {
let account = self.sstore.account_ref(&address)?;
Ok(AccountMeta {
name: self.sstore.name(&account)?,
meta: self.sstore.meta(&account)?,
uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a Uuid
})
}

/// Returns account public key.
pub fn account_public(&self, address: Address, password: &str) -> Result<Public, Error> {
self.sstore.public(&self.sstore.account_ref(&address)?, password)
Expand Down Expand Up @@ -810,6 +866,7 @@ impl AccountProvider {
}

/// Sign transaction with hardware wallet.
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
pub fn sign_with_hardware(&self, address: Address, transaction: &Transaction, chain_id: Option<u64>, rlp_encoded_transaction: &[u8]) -> Result<Signature, SignError> {
let t_info = TransactionInfo {
nonce: transaction.nonce,
Expand Down
9 changes: 7 additions & 2 deletions ethcore/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ extern crate ethcore_transaction as transaction;
extern crate ethereum_types;
extern crate ethjson;
extern crate ethkey;
extern crate hardware_wallet;


extern crate hashdb;
extern crate itertools;
extern crate lru_cache;
Expand All @@ -98,7 +99,6 @@ extern crate kvdb;
extern crate kvdb_memorydb;
extern crate util_error;
extern crate snappy;

extern crate ethabi;
extern crate rustc_hex;
extern crate stats;
Expand All @@ -108,6 +108,11 @@ extern crate vm;
extern crate wasm;
extern crate memory_cache;
extern crate journaldb;


#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
extern crate hardware_wallet;

#[cfg(test)]
extern crate tempdir;

Expand Down
3 changes: 3 additions & 0 deletions rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ extern crate parity_version as version;
extern crate rlp;
extern crate stats;
extern crate keccak_hash as hash;

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
extern crate hardware_wallet;

extern crate patricia_trie as trie;

#[macro_use]
Expand Down
85 changes: 83 additions & 2 deletions rpc/src/v1/helpers/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ use light::cache::Cache as LightDataCache;
use light::client::LightChainClient;
use light::on_demand::{request, OnDemand};
use light::TransactionQueue as LightTransactionQueue;
use rlp;
use hash::keccak;
use ethereum_types::{H256, H520, Address, U256};
use bytes::Bytes;
Expand Down Expand Up @@ -53,6 +52,9 @@ use v1::types::{
DecryptRequest as RpcDecryptRequest,
};

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
use rlp;

pub use self::nonce::Reservations;

/// Has the capability to dispatch, sign, and decrypt.
Expand Down Expand Up @@ -320,7 +322,7 @@ impl LightDispatcher {
x.map(move |acc| acc.map_or(account_start_nonce, |acc| acc.nonce))
.map_err(|_| errors::no_light_peers())
),
None => Box::new(future::err(errors::network_disabled()))
None => Box::new(future::err(errors::network_disabled()))
}
}
}
Expand Down Expand Up @@ -415,6 +417,7 @@ impl Dispatcher for LightDispatcher {
}
}

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
fn sign_transaction(
accounts: &AccountProvider,
filled: FilledTransactionRequest,
Expand Down Expand Up @@ -444,6 +447,32 @@ fn sign_transaction(
}))
}

#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android")))]
fn sign_transaction(
accounts: &AccountProvider,
filled: FilledTransactionRequest,
chain_id: Option<u64>,
nonce: U256,
password: SignWith,
) -> Result<WithToken<SignedTransaction>> {
let t = Transaction {
nonce: nonce,
action: filled.to.map_or(Action::Create, Action::Call),
gas: filled.gas,
gas_price: filled.gas_price,
value: filled.value,
data: filled.data,
};

let hash = t.hash(chain_id);
let signature = signature(accounts, filled.from, hash, password)?;

Ok(signature.map(|sig| {
SignedTransaction::new(t.with_signature(sig, chain_id))
.expect("Transaction was signed by AccountsProvider; it never produces invalid signatures; qed")
}))
}

#[derive(Debug, Clone, Copy)]
enum ProspectiveSignerState {
TryProspectiveSign,
Expand Down Expand Up @@ -645,6 +674,7 @@ impl<T: Debug> From<(T, Option<AccountToken>)> for WithToken<T> {
}

/// Execute a confirmation payload.
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
pub fn execute<D: Dispatcher + 'static>(
dispatcher: D,
accounts: Arc<AccountProvider>,
Expand Down Expand Up @@ -701,6 +731,56 @@ pub fn execute<D: Dispatcher + 'static>(
}
}

/// Execute a confirmation payload without a hardware wallet
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android")))]
pub fn execute<D: Dispatcher + 'static>(
dispatcher: D,
accounts: Arc<AccountProvider>,
payload: ConfirmationPayload,
pass: SignWith
) -> BoxFuture<WithToken<ConfirmationResponse>> {
match payload {
ConfirmationPayload::SendTransaction(request) => {
let condition = request.condition.clone().map(Into::into);
Box::new(dispatcher.sign(accounts, request, pass)
.map(move |v| v.map(move |tx| PendingTransaction::new(tx, condition)))
.map(WithToken::into_tuple)
.map(|(tx, token)| (tx, token, dispatcher))
.and_then(|(tx, tok, dispatcher)| {
dispatcher.dispatch_transaction(tx)
.map(RpcH256::from)
.map(ConfirmationResponse::SendTransaction)
.map(move |h| WithToken::from((h, tok)))
}))
},
ConfirmationPayload::SignTransaction(request) => {
Box::new(dispatcher.sign(accounts, request, pass)
.map(move |result| result
.map(move |tx| dispatcher.enrich(tx))
.map(ConfirmationResponse::SignTransaction)
))
},
ConfirmationPayload::EthSignMessage(address, data) => {
let hash = eth_data_hash(data);
let res = signature(&accounts, address, hash, pass)
.map(|result| result
.map(|rsv| H520(rsv.into_electrum()))
.map(RpcH520::from)
.map(ConfirmationResponse::Signature)
);
Box::new(future::done(res))
},
ConfirmationPayload::Decrypt(address, data) => {
let res = decrypt(&accounts, address, data, pass)
.map(|result| result
.map(RpcBytes)
.map(ConfirmationResponse::Decrypt)
);
Box::new(future::done(res))
},
}
}

fn signature(accounts: &AccountProvider, address: Address, hash: H256, password: SignWith) -> Result<WithToken<Signature>> {
match password.clone() {
SignWith::Nothing => accounts.sign(address, None, hash).map(WithToken::No),
Expand All @@ -713,6 +793,7 @@ fn signature(accounts: &AccountProvider, address: Address, hash: H256, password:
}

// obtain a hardware signature from the given account.
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))]
fn hardware_signature(accounts: &AccountProvider, address: Address, t: Transaction, chain_id: Option<u64>)
-> Result<SignedTransaction>
{
Expand Down
Loading

0 comments on commit d166498

Please sign in to comment.