Remove support for hardware wallets (#10678)
This commit is contained in:
		
							parent
							
								
									c270599a23
								
							
						
					
					
						commit
						11fb967c6a
					
				
							
								
								
									
										72
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										72
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@ -909,8 +909,6 @@ dependencies = [
 | 
			
		||||
 "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "ethkey 0.3.0",
 | 
			
		||||
 "ethstore 0.2.1",
 | 
			
		||||
 "fake-hardware-wallet 0.0.1",
 | 
			
		||||
 "hardware-wallet 1.12.0",
 | 
			
		||||
 "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
@ -1502,14 +1500,6 @@ dependencies = [
 | 
			
		||||
 "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "fake-hardware-wallet"
 | 
			
		||||
version = "0.0.1"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "ethkey 0.3.0",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "fake-simd"
 | 
			
		||||
version = "0.1.2"
 | 
			
		||||
@ -1696,22 +1686,6 @@ name = "hamming"
 | 
			
		||||
version = "0.1.3"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "hardware-wallet"
 | 
			
		||||
version = "1.12.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "ethkey 0.3.0",
 | 
			
		||||
 "hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)",
 | 
			
		||||
 "libusb 0.3.0 (git+https://github.com/paritytech/libusb-rs)",
 | 
			
		||||
 "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "protobuf 1.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "trezor-sys 1.0.0 (git+https://github.com/paritytech/trezor-sys)",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "hash-db"
 | 
			
		||||
version = "0.11.0"
 | 
			
		||||
@ -1764,15 +1738,6 @@ dependencies = [
 | 
			
		||||
 "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "hidapi"
 | 
			
		||||
version = "0.3.1"
 | 
			
		||||
source = "git+https://github.com/paritytech/hidapi-rs#d4d323767d6f27cf5a3d73fbae0b0f2134d579bf"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "hmac"
 | 
			
		||||
version = "0.6.3"
 | 
			
		||||
@ -2270,25 +2235,6 @@ dependencies = [
 | 
			
		||||
 "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "libusb"
 | 
			
		||||
version = "0.3.0"
 | 
			
		||||
source = "git+https://github.com/paritytech/libusb-rs#442708954a720bc89a9cf41e7be021a778bdbc27"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "libusb-sys 0.2.5 (git+https://github.com/paritytech/libusb-sys)",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "libusb-sys"
 | 
			
		||||
version = "0.2.5"
 | 
			
		||||
source = "git+https://github.com/paritytech/libusb-sys#f49d8fc7ca43a465c721c8eafc37c86fd06f795f"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "linked-hash-map"
 | 
			
		||||
version = "0.5.2"
 | 
			
		||||
@ -3384,11 +3330,6 @@ dependencies = [
 | 
			
		||||
 "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "protobuf"
 | 
			
		||||
version = "1.7.5"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "pulldown-cmark"
 | 
			
		||||
version = "0.0.3"
 | 
			
		||||
@ -4580,14 +4521,6 @@ name = "transient-hashmap"
 | 
			
		||||
version = "0.4.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "trezor-sys"
 | 
			
		||||
version = "1.0.0"
 | 
			
		||||
source = "git+https://github.com/paritytech/trezor-sys#8a401705e58c83db6c29c199d9577b78fde40709"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "protobuf 1.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "trie-db"
 | 
			
		||||
version = "0.11.0"
 | 
			
		||||
@ -5141,7 +5074,6 @@ dependencies = [
 | 
			
		||||
"checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa"
 | 
			
		||||
"checksum hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0"
 | 
			
		||||
"checksum hex-literal-impl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d4c5c844e2fee0bf673d54c2c177f1713b3d2af2ff6e666b49cb7572e6cf42d"
 | 
			
		||||
"checksum hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)" = "<none>"
 | 
			
		||||
"checksum hmac 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "733e1b3ac906631ca01ebb577e9bb0f5e37a454032b9036b5eaea4013ed6f99a"
 | 
			
		||||
"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695"
 | 
			
		||||
"checksum home 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "29302b90cfa76231a757a887d1e3153331a63c7f80b6c75f86366334cbe70708"
 | 
			
		||||
@ -5188,8 +5120,6 @@ dependencies = [
 | 
			
		||||
"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
 | 
			
		||||
"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8"
 | 
			
		||||
"checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753"
 | 
			
		||||
"checksum libusb 0.3.0 (git+https://github.com/paritytech/libusb-rs)" = "<none>"
 | 
			
		||||
"checksum libusb-sys 0.2.5 (git+https://github.com/paritytech/libusb-sys)" = "<none>"
 | 
			
		||||
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
 | 
			
		||||
"checksum local-encoding 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1ceb20f39ff7ae42f3ff9795f3986b1daad821caaa1e1732a0944103a5a1a66"
 | 
			
		||||
"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c"
 | 
			
		||||
@ -5269,7 +5199,6 @@ dependencies = [
 | 
			
		||||
"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5"
 | 
			
		||||
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
 | 
			
		||||
"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27"
 | 
			
		||||
"checksum protobuf 1.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e14ccd6b79ec748412d4f2dfde1a80fa363a67def4062969f8aed3d790a30f28"
 | 
			
		||||
"checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07"
 | 
			
		||||
"checksum pwasm-utils 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "efb0dcbddbb600f47a7098d33762a00552c671992171637f5bb310b37fe1f0e4"
 | 
			
		||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
 | 
			
		||||
@ -5396,7 +5325,6 @@ dependencies = [
 | 
			
		||||
"checksum trace-time 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c9adf04084eeb9a1ea91be6c3f8ef3df392391c91fc7d8f696d4875f6754e715"
 | 
			
		||||
"checksum transaction-pool 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d8bbee24c711a878e7d8f89460569034cacf2d8c58dde785b5ffa06ed6b59663"
 | 
			
		||||
"checksum transient-hashmap 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aeb4b191d033a35edfce392a38cdcf9790b6cebcb30fa690c312c29da4dc433e"
 | 
			
		||||
"checksum trezor-sys 1.0.0 (git+https://github.com/paritytech/trezor-sys)" = "<none>"
 | 
			
		||||
"checksum trie-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7319e28ca295f27359d944a682f7f65b419158bf1590c92cadc0000258d788"
 | 
			
		||||
"checksum trie-standardmap 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e26f52976a57a0859616d6fcec87092ac35d08eabbd78dc3dabee93b480ea5f"
 | 
			
		||||
"checksum triehash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d26efb4ddf87870fc08dc9a6580dc3061be350d7b9d0eb30aef1c8b4227aa46"
 | 
			
		||||
 | 
			
		||||
@ -57,7 +57,7 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do
 | 
			
		||||
  $ curl https://sh.rustup.rs -sSf | sh
 | 
			
		||||
  ```
 | 
			
		||||
 | 
			
		||||
  Parity Ethereum also requires `gcc`, `g++`, `libudev-dev`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed.
 | 
			
		||||
  Parity Ethereum also requires `gcc`, `g++`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed.
 | 
			
		||||
 | 
			
		||||
- OSX:
 | 
			
		||||
  ```bash
 | 
			
		||||
 | 
			
		||||
@ -17,12 +17,6 @@ serde = "1.0"
 | 
			
		||||
serde_derive = "1.0"
 | 
			
		||||
serde_json = "1.0"
 | 
			
		||||
 | 
			
		||||
[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))'.dependencies]
 | 
			
		||||
hardware-wallet = { path = "hw" }
 | 
			
		||||
 | 
			
		||||
[target.'cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))'.dependencies]
 | 
			
		||||
fake-hardware-wallet = { path = "fake-hardware-wallet" }
 | 
			
		||||
 | 
			
		||||
[dev-dependencies]
 | 
			
		||||
ethereum-types = "0.4"
 | 
			
		||||
tempdir = "0.3"
 | 
			
		||||
 | 
			
		||||
@ -1,10 +0,0 @@
 | 
			
		||||
[package]
 | 
			
		||||
description = "Fake hardware-wallet, for OS' that don't support libusb"
 | 
			
		||||
name = "fake-hardware-wallet"
 | 
			
		||||
version = "0.0.1"
 | 
			
		||||
license = "GPL-3.0"
 | 
			
		||||
authors = ["Parity Technologies <admin@parity.io>"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
ethereum-types = "0.4"
 | 
			
		||||
ethkey = { path = "../../accounts/ethkey" }
 | 
			
		||||
@ -1,107 +0,0 @@
 | 
			
		||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
 | 
			
		||||
// This file is part of Parity Ethereum.
 | 
			
		||||
 | 
			
		||||
// Parity Ethereum 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 Ethereum 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 Ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
//! Dummy module for platforms that does not provide support for hardware wallets (libusb)
 | 
			
		||||
 | 
			
		||||
extern crate ethereum_types;
 | 
			
		||||
extern crate ethkey;
 | 
			
		||||
 | 
			
		||||
use ethereum_types::U256;
 | 
			
		||||
use ethkey::{Address, Signature};
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
pub struct WalletInfo {
 | 
			
		||||
    pub address: Address,
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub manufacturer: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
/// `ErrorType` for devices with no `hardware wallet`
 | 
			
		||||
pub enum Error {
 | 
			
		||||
    NoWallet,
 | 
			
		||||
    KeyNotFound,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct TransactionInfo {
 | 
			
		||||
    /// Nonce
 | 
			
		||||
    pub nonce: U256,
 | 
			
		||||
    /// Gas price
 | 
			
		||||
    pub gas_price: U256,
 | 
			
		||||
    /// Gas limit
 | 
			
		||||
    pub gas_limit: U256,
 | 
			
		||||
    /// Receiver
 | 
			
		||||
    pub to: Option<Address>,
 | 
			
		||||
    /// Value
 | 
			
		||||
    pub value: U256,
 | 
			
		||||
    /// Data
 | 
			
		||||
    pub data: Vec<u8>,
 | 
			
		||||
    /// Chain ID
 | 
			
		||||
    pub chain_id: Option<u64>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum KeyPath {
 | 
			
		||||
    /// Ethereum.
 | 
			
		||||
    Ethereum,
 | 
			
		||||
    /// Ethereum classic.
 | 
			
		||||
    EthereumClassic,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// `HardwareWalletManager` for devices with no `hardware wallet`
 | 
			
		||||
pub struct HardwareWalletManager;
 | 
			
		||||
 | 
			
		||||
impl HardwareWalletManager {
 | 
			
		||||
    pub fn new() -> Result<Self, Error> {
 | 
			
		||||
        Err(Error::NoWallet)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_key_path(&self, _key_path: KeyPath) {}
 | 
			
		||||
 | 
			
		||||
    pub fn wallet_info(&self, _: &Address) -> Option<WalletInfo> {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn list_wallets(&self) -> Vec<WalletInfo> {
 | 
			
		||||
        Vec::with_capacity(0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn list_locked_wallets(&self) -> Result<Vec<String>, Error> {
 | 
			
		||||
        Err(Error::NoWallet)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn pin_matrix_ack(&self, _: &str, _: &str) -> Result<bool, Error> {
 | 
			
		||||
        Err(Error::NoWallet)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn sign_transaction(
 | 
			
		||||
        &self,
 | 
			
		||||
        _address: &Address,
 | 
			
		||||
        _transaction: &TransactionInfo,
 | 
			
		||||
        _rlp_transaction: &[u8],
 | 
			
		||||
    ) -> Result<Signature, Error> {
 | 
			
		||||
        Err(Error::NoWallet)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn sign_message(&self, _address: &Address, _msg: &[u8]) -> Result<Signature, Error> {
 | 
			
		||||
        Err(Error::NoWallet)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Error {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
			
		||||
        write!(f, "No hardware wallet!!")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,21 +0,0 @@
 | 
			
		||||
[package]
 | 
			
		||||
description = "Hardware wallet support."
 | 
			
		||||
homepage = "http://parity.io"
 | 
			
		||||
license = "GPL-3.0"
 | 
			
		||||
name = "hardware-wallet"
 | 
			
		||||
version = "1.12.0"
 | 
			
		||||
authors = ["Parity Technologies <admin@parity.io>"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
log = "0.4"
 | 
			
		||||
parking_lot = "0.7"
 | 
			
		||||
protobuf = "1.4"
 | 
			
		||||
hidapi = { git = "https://github.com/paritytech/hidapi-rs" }
 | 
			
		||||
libusb = { git = "https://github.com/paritytech/libusb-rs" }
 | 
			
		||||
trezor-sys = { git = "https://github.com/paritytech/trezor-sys" }
 | 
			
		||||
ethkey = { path = "../ethkey" }
 | 
			
		||||
ethereum-types = "0.4"
 | 
			
		||||
semver = "0.9"
 | 
			
		||||
 | 
			
		||||
[dev-dependencies]
 | 
			
		||||
rustc-hex = "1.0"
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -1,443 +0,0 @@
 | 
			
		||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
 | 
			
		||||
// This file is part of Parity Ethereum.
 | 
			
		||||
 | 
			
		||||
// Parity Ethereum 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 Ethereum 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 Ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
//! Hardware wallet management.
 | 
			
		||||
 | 
			
		||||
#![warn(missing_docs)]
 | 
			
		||||
#![warn(warnings)]
 | 
			
		||||
 | 
			
		||||
extern crate ethereum_types;
 | 
			
		||||
extern crate ethkey;
 | 
			
		||||
extern crate hidapi;
 | 
			
		||||
extern crate libusb;
 | 
			
		||||
extern crate parking_lot;
 | 
			
		||||
extern crate protobuf;
 | 
			
		||||
extern crate semver;
 | 
			
		||||
extern crate trezor_sys;
 | 
			
		||||
 | 
			
		||||
#[macro_use]
 | 
			
		||||
extern crate log;
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
extern crate rustc_hex;
 | 
			
		||||
 | 
			
		||||
mod ledger;
 | 
			
		||||
mod trezor;
 | 
			
		||||
 | 
			
		||||
use std::{
 | 
			
		||||
    fmt,
 | 
			
		||||
    sync::{atomic, atomic::AtomicBool, Arc, Weak},
 | 
			
		||||
    thread,
 | 
			
		||||
    time::Duration,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use ethereum_types::U256;
 | 
			
		||||
use ethkey::{Address, Signature};
 | 
			
		||||
use parking_lot::Mutex;
 | 
			
		||||
 | 
			
		||||
const HID_GLOBAL_USAGE_PAGE: u16 = 0xFF00;
 | 
			
		||||
const HID_USB_DEVICE_CLASS: u8 = 0;
 | 
			
		||||
const MAX_POLLING_DURATION: Duration = Duration::from_millis(500);
 | 
			
		||||
const USB_EVENT_POLLING_INTERVAL: Duration = Duration::from_millis(500);
 | 
			
		||||
 | 
			
		||||
/// `HardwareWallet` device
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct Device {
 | 
			
		||||
    path: String,
 | 
			
		||||
    info: WalletInfo,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// `Wallet` trait
 | 
			
		||||
pub trait Wallet<'a> {
 | 
			
		||||
    /// Error
 | 
			
		||||
    type Error;
 | 
			
		||||
    /// Transaction data format
 | 
			
		||||
    type Transaction;
 | 
			
		||||
 | 
			
		||||
    /// Sign transaction data with wallet managing `address`.
 | 
			
		||||
    fn sign_transaction(
 | 
			
		||||
        &self,
 | 
			
		||||
        address: &Address,
 | 
			
		||||
        transaction: Self::Transaction,
 | 
			
		||||
    ) -> Result<Signature, Self::Error>;
 | 
			
		||||
 | 
			
		||||
    /// Set key derivation path for a chain.
 | 
			
		||||
    fn set_key_path(&self, key_path: KeyPath);
 | 
			
		||||
 | 
			
		||||
    /// Re-populate device list
 | 
			
		||||
    /// Note, this assumes all devices are iterated over and updated
 | 
			
		||||
    fn update_devices(&self, device_direction: DeviceDirection) -> Result<usize, Self::Error>;
 | 
			
		||||
 | 
			
		||||
    /// Read device info
 | 
			
		||||
    fn read_device(
 | 
			
		||||
        &self,
 | 
			
		||||
        usb: &hidapi::HidApi,
 | 
			
		||||
        dev_info: &hidapi::HidDeviceInfo,
 | 
			
		||||
    ) -> Result<Device, Self::Error>;
 | 
			
		||||
 | 
			
		||||
    /// List connected and acknowledged wallets
 | 
			
		||||
    fn list_devices(&self) -> Vec<WalletInfo>;
 | 
			
		||||
 | 
			
		||||
    /// List locked wallets
 | 
			
		||||
    /// This may be moved if it is the wrong assumption, for example this is not supported by Ledger
 | 
			
		||||
    /// Then this method return a empty vector
 | 
			
		||||
    fn list_locked_devices(&self) -> Vec<String>;
 | 
			
		||||
 | 
			
		||||
    /// Get wallet info.
 | 
			
		||||
    fn get_wallet(&self, address: &Address) -> Option<WalletInfo>;
 | 
			
		||||
 | 
			
		||||
    /// Generate ethereum address for a Wallet
 | 
			
		||||
    fn get_address(&self, device: &hidapi::HidDevice) -> Result<Option<Address>, Self::Error>;
 | 
			
		||||
 | 
			
		||||
    /// Open a device using `device path`
 | 
			
		||||
    /// Note, f - is a closure that borrows HidResult<HidDevice>
 | 
			
		||||
    /// HidDevice is in turn a type alias for a `c_void function pointer`
 | 
			
		||||
    /// For further information see:
 | 
			
		||||
    ///		* <https://github.com/paritytech/hidapi-rs>
 | 
			
		||||
    ///		* <https://github.com/rust-lang/libc>
 | 
			
		||||
    fn open_path<R, F>(&self, f: F) -> Result<R, Self::Error>
 | 
			
		||||
    where
 | 
			
		||||
        F: Fn() -> Result<R, &'static str>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Hardware wallet error.
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum Error {
 | 
			
		||||
    /// Ledger device error.
 | 
			
		||||
    LedgerDevice(ledger::Error),
 | 
			
		||||
    /// Trezor device error
 | 
			
		||||
    TrezorDevice(trezor::Error),
 | 
			
		||||
    /// USB error.
 | 
			
		||||
    Usb(libusb::Error),
 | 
			
		||||
    /// HID error
 | 
			
		||||
    Hid(String),
 | 
			
		||||
    /// Hardware wallet not found for specified key.
 | 
			
		||||
    KeyNotFound,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// This is the transaction info we need to supply to Trezor message. It's more
 | 
			
		||||
/// or less a duplicate of `ethcore::transaction::Transaction`, but we can't
 | 
			
		||||
/// import ethcore here as that would be a circular dependency.
 | 
			
		||||
pub struct TransactionInfo {
 | 
			
		||||
    /// Nonce
 | 
			
		||||
    pub nonce: U256,
 | 
			
		||||
    /// Gas price
 | 
			
		||||
    pub gas_price: U256,
 | 
			
		||||
    /// Gas limit
 | 
			
		||||
    pub gas_limit: U256,
 | 
			
		||||
    /// Receiver
 | 
			
		||||
    pub to: Option<Address>,
 | 
			
		||||
    /// Value
 | 
			
		||||
    pub value: U256,
 | 
			
		||||
    /// Data
 | 
			
		||||
    pub data: Vec<u8>,
 | 
			
		||||
    /// Chain ID
 | 
			
		||||
    pub chain_id: Option<u64>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Hardware wallet information.
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct WalletInfo {
 | 
			
		||||
    /// Wallet device name.
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    /// Wallet device manufacturer.
 | 
			
		||||
    pub manufacturer: String,
 | 
			
		||||
    /// Wallet device serial number.
 | 
			
		||||
    pub serial: String,
 | 
			
		||||
    /// Ethereum address.
 | 
			
		||||
    pub address: Address,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Key derivation paths used on hardware wallets.
 | 
			
		||||
#[derive(Debug, Clone, Copy)]
 | 
			
		||||
pub enum KeyPath {
 | 
			
		||||
    /// Ethereum.
 | 
			
		||||
    Ethereum,
 | 
			
		||||
    /// Ethereum classic.
 | 
			
		||||
    EthereumClassic,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Error {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Error::KeyNotFound => write!(f, "Key not found for given address."),
 | 
			
		||||
            Error::LedgerDevice(ref e) => write!(f, "{}", e),
 | 
			
		||||
            Error::TrezorDevice(ref e) => write!(f, "{}", e),
 | 
			
		||||
            Error::Usb(ref e) => write!(f, "{}", e),
 | 
			
		||||
            Error::Hid(ref e) => write!(f, "{}", e),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<ledger::Error> for Error {
 | 
			
		||||
    fn from(err: ledger::Error) -> Self {
 | 
			
		||||
        match err {
 | 
			
		||||
            ledger::Error::KeyNotFound => Error::KeyNotFound,
 | 
			
		||||
            _ => Error::LedgerDevice(err),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<trezor::Error> for Error {
 | 
			
		||||
    fn from(err: trezor::Error) -> Self {
 | 
			
		||||
        match err {
 | 
			
		||||
            trezor::Error::KeyNotFound => Error::KeyNotFound,
 | 
			
		||||
            _ => Error::TrezorDevice(err),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<libusb::Error> for Error {
 | 
			
		||||
    fn from(err: libusb::Error) -> Self {
 | 
			
		||||
        Error::Usb(err)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Specifies the direction of the `HardwareWallet` i.e, whether it arrived or left
 | 
			
		||||
#[derive(Debug, Copy, Clone, PartialEq)]
 | 
			
		||||
pub enum DeviceDirection {
 | 
			
		||||
    /// Device arrived
 | 
			
		||||
    Arrived,
 | 
			
		||||
    /// Device left
 | 
			
		||||
    Left,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for DeviceDirection {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            DeviceDirection::Arrived => write!(f, "arrived"),
 | 
			
		||||
            DeviceDirection::Left => write!(f, "left"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Hardware wallet management interface.
 | 
			
		||||
pub struct HardwareWalletManager {
 | 
			
		||||
    exiting: Arc<AtomicBool>,
 | 
			
		||||
    ledger: Arc<ledger::Manager>,
 | 
			
		||||
    trezor: Arc<trezor::Manager>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl HardwareWalletManager {
 | 
			
		||||
    /// Hardware wallet constructor
 | 
			
		||||
    pub fn new() -> Result<Self, Error> {
 | 
			
		||||
        let exiting = Arc::new(AtomicBool::new(false));
 | 
			
		||||
        let hidapi = Arc::new(Mutex::new(
 | 
			
		||||
            hidapi::HidApi::new().map_err(|e| Error::Hid(e.to_string().clone()))?,
 | 
			
		||||
        ));
 | 
			
		||||
        let ledger = ledger::Manager::new(hidapi.clone());
 | 
			
		||||
        let trezor = trezor::Manager::new(hidapi.clone());
 | 
			
		||||
        let usb_context = Arc::new(libusb::Context::new()?);
 | 
			
		||||
 | 
			
		||||
        let l = ledger.clone();
 | 
			
		||||
        let t = trezor.clone();
 | 
			
		||||
        let exit = exiting.clone();
 | 
			
		||||
 | 
			
		||||
        // Subscribe to all vendor IDs (VIDs) and product IDs (PIDs)
 | 
			
		||||
        // This means that the `HardwareWalletManager` is responsible to validate the detected device
 | 
			
		||||
        usb_context.register_callback(
 | 
			
		||||
            None,
 | 
			
		||||
            None,
 | 
			
		||||
            Some(HID_USB_DEVICE_CLASS),
 | 
			
		||||
            Box::new(EventHandler::new(
 | 
			
		||||
                Arc::downgrade(&ledger),
 | 
			
		||||
                Arc::downgrade(&trezor),
 | 
			
		||||
            )),
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        // Hardware event subscriber thread
 | 
			
		||||
        thread::Builder::new()
 | 
			
		||||
            .name("hw_wallet_manager".to_string())
 | 
			
		||||
            .spawn(move || {
 | 
			
		||||
                if let Err(e) = l.update_devices(DeviceDirection::Arrived) {
 | 
			
		||||
                    debug!(target: "hw", "Ledger couldn't connect at startup, error: {}", e);
 | 
			
		||||
                }
 | 
			
		||||
                if let Err(e) = t.update_devices(DeviceDirection::Arrived) {
 | 
			
		||||
                    debug!(target: "hw", "Trezor couldn't connect at startup, error: {}", e);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                while !exit.load(atomic::Ordering::Acquire) {
 | 
			
		||||
                    if let Err(e) = usb_context.handle_events(Some(USB_EVENT_POLLING_INTERVAL)) {
 | 
			
		||||
                        debug!(target: "hw", "HardwareWalletManager event handler error: {}", e);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            .ok();
 | 
			
		||||
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            exiting,
 | 
			
		||||
            trezor,
 | 
			
		||||
            ledger,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Select key derivation path for a chain.
 | 
			
		||||
    /// Currently, only one hard-coded keypath is supported
 | 
			
		||||
    /// It is managed by `ethcore/account_provider`
 | 
			
		||||
    pub fn set_key_path(&self, key_path: KeyPath) {
 | 
			
		||||
        self.ledger.set_key_path(key_path);
 | 
			
		||||
        self.trezor.set_key_path(key_path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// List connected wallets. This only returns wallets that are ready to be used.
 | 
			
		||||
    pub fn list_wallets(&self) -> Vec<WalletInfo> {
 | 
			
		||||
        let mut wallets = Vec::new();
 | 
			
		||||
        wallets.extend(self.ledger.list_devices());
 | 
			
		||||
        wallets.extend(self.trezor.list_devices());
 | 
			
		||||
        wallets
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return a list of paths to locked hardware wallets
 | 
			
		||||
    /// This is only applicable to Trezor because Ledger only appears as
 | 
			
		||||
    /// a device when it is unlocked
 | 
			
		||||
    pub fn list_locked_wallets(&self) -> Result<Vec<String>, Error> {
 | 
			
		||||
        Ok(self.trezor.list_locked_devices())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get connected wallet info.
 | 
			
		||||
    pub fn wallet_info(&self, address: &Address) -> Option<WalletInfo> {
 | 
			
		||||
        if let Some(info) = self.ledger.get_wallet(address) {
 | 
			
		||||
            Some(info)
 | 
			
		||||
        } else {
 | 
			
		||||
            self.trezor.get_wallet(address)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sign a message with the wallet (only supported by Ledger)
 | 
			
		||||
    pub fn sign_message(&self, address: &Address, msg: &[u8]) -> Result<Signature, Error> {
 | 
			
		||||
        if self.ledger.get_wallet(address).is_some() {
 | 
			
		||||
            Ok(self.ledger.sign_message(address, msg)?)
 | 
			
		||||
        } else if self.trezor.get_wallet(address).is_some() {
 | 
			
		||||
            Err(Error::TrezorDevice(trezor::Error::NoSigningMessage))
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(Error::KeyNotFound)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sign transaction data with wallet managing `address`.
 | 
			
		||||
    pub fn sign_transaction(
 | 
			
		||||
        &self,
 | 
			
		||||
        address: &Address,
 | 
			
		||||
        t_info: &TransactionInfo,
 | 
			
		||||
        encoded_transaction: &[u8],
 | 
			
		||||
    ) -> Result<Signature, Error> {
 | 
			
		||||
        if self.ledger.get_wallet(address).is_some() {
 | 
			
		||||
            Ok(self.ledger.sign_transaction(address, encoded_transaction)?)
 | 
			
		||||
        } else if self.trezor.get_wallet(address).is_some() {
 | 
			
		||||
            Ok(self.trezor.sign_transaction(address, t_info)?)
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(Error::KeyNotFound)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Send a pin to a device at a certain path to unlock it
 | 
			
		||||
    /// This is only applicable to Trezor because Ledger only appears as
 | 
			
		||||
    /// a device when it is unlocked
 | 
			
		||||
    pub fn pin_matrix_ack(&self, path: &str, pin: &str) -> Result<bool, Error> {
 | 
			
		||||
        self.trezor
 | 
			
		||||
            .pin_matrix_ack(path, pin)
 | 
			
		||||
            .map_err(Error::TrezorDevice)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for HardwareWalletManager {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        // Indicate to the USB Hotplug handler that it
 | 
			
		||||
        // shall terminate but don't wait for it to terminate.
 | 
			
		||||
        // If it doesn't terminate for some reason USB Hotplug events will be handled
 | 
			
		||||
        // even if the HardwareWalletManger has been dropped
 | 
			
		||||
        self.exiting.store(true, atomic::Ordering::Release);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Hardware wallet event handler
 | 
			
		||||
///
 | 
			
		||||
/// Note, that this runs to completion and race-conditions can't occur but it can
 | 
			
		||||
/// stop other events for being processed with an infinite loop or similar
 | 
			
		||||
struct EventHandler {
 | 
			
		||||
    ledger: Weak<ledger::Manager>,
 | 
			
		||||
    trezor: Weak<trezor::Manager>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl EventHandler {
 | 
			
		||||
    /// Trezor event handler constructor
 | 
			
		||||
    pub fn new(ledger: Weak<ledger::Manager>, trezor: Weak<trezor::Manager>) -> Self {
 | 
			
		||||
        Self { ledger, trezor }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn extract_device_info(device: &libusb::Device) -> Result<(u16, u16), Error> {
 | 
			
		||||
        let desc = device.device_descriptor()?;
 | 
			
		||||
        Ok((desc.vendor_id(), desc.product_id()))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl libusb::Hotplug for EventHandler {
 | 
			
		||||
    fn device_arrived(&mut self, device: libusb::Device) {
 | 
			
		||||
        // Upgrade reference to an Arc
 | 
			
		||||
        if let (Some(ledger), Some(trezor)) = (self.ledger.upgrade(), self.trezor.upgrade()) {
 | 
			
		||||
            // Version ID and Product ID are available
 | 
			
		||||
            if let Ok((vid, pid)) = Self::extract_device_info(&device) {
 | 
			
		||||
                if trezor::is_valid_trezor(vid, pid) {
 | 
			
		||||
                    if !trezor::try_connect_polling(
 | 
			
		||||
                        &trezor,
 | 
			
		||||
                        &MAX_POLLING_DURATION,
 | 
			
		||||
                        DeviceDirection::Arrived,
 | 
			
		||||
                    ) {
 | 
			
		||||
                        trace!(target: "hw", "Trezor device was detected but connection failed");
 | 
			
		||||
                    }
 | 
			
		||||
                } else if ledger::is_valid_ledger(vid, pid) {
 | 
			
		||||
                    if !ledger::try_connect_polling(
 | 
			
		||||
                        &ledger,
 | 
			
		||||
                        &MAX_POLLING_DURATION,
 | 
			
		||||
                        DeviceDirection::Arrived,
 | 
			
		||||
                    ) {
 | 
			
		||||
                        trace!(target: "hw", "Ledger device was detected but connection failed");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn device_left(&mut self, device: libusb::Device) {
 | 
			
		||||
        // Upgrade reference to an Arc
 | 
			
		||||
        if let (Some(ledger), Some(trezor)) = (self.ledger.upgrade(), self.trezor.upgrade()) {
 | 
			
		||||
            // Version ID and Product ID are available
 | 
			
		||||
            if let Ok((vid, pid)) = Self::extract_device_info(&device) {
 | 
			
		||||
                if trezor::is_valid_trezor(vid, pid) {
 | 
			
		||||
                    if !trezor::try_connect_polling(
 | 
			
		||||
                        &trezor,
 | 
			
		||||
                        &MAX_POLLING_DURATION,
 | 
			
		||||
                        DeviceDirection::Left,
 | 
			
		||||
                    ) {
 | 
			
		||||
                        trace!(target: "hw", "Trezor device was detected but disconnection failed");
 | 
			
		||||
                    }
 | 
			
		||||
                } else if ledger::is_valid_ledger(vid, pid) {
 | 
			
		||||
                    if !ledger::try_connect_polling(
 | 
			
		||||
                        &ledger,
 | 
			
		||||
                        &MAX_POLLING_DURATION,
 | 
			
		||||
                        DeviceDirection::Left,
 | 
			
		||||
                    ) {
 | 
			
		||||
                        trace!(target: "hw", "Ledger device was detected but disconnection failed");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Helper to determine if a device is a valid HID
 | 
			
		||||
pub fn is_valid_hid_device(usage_page: u16, interface_number: i32) -> bool {
 | 
			
		||||
    usage_page == HID_GLOBAL_USAGE_PAGE || interface_number == HID_USB_DEVICE_CLASS as i32
 | 
			
		||||
}
 | 
			
		||||
@ -1,540 +0,0 @@
 | 
			
		||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
 | 
			
		||||
// This file is part of Parity Ethereum.
 | 
			
		||||
 | 
			
		||||
// Parity Ethereum 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 Ethereum 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 Ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
//! Trezor hardware wallet module. Supports Trezor v1.
 | 
			
		||||
//! See <http://doc.satoshilabs.com/trezor-tech/api-protobuf.html>
 | 
			
		||||
//! and <https://github.com/trezor/trezor-common/blob/master/protob/protocol.md>
 | 
			
		||||
//! for protocol details.
 | 
			
		||||
 | 
			
		||||
use std::{
 | 
			
		||||
    cmp::{max, min},
 | 
			
		||||
    fmt,
 | 
			
		||||
    sync::Arc,
 | 
			
		||||
    time::{Duration, Instant},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use super::{
 | 
			
		||||
    is_valid_hid_device, Device, DeviceDirection, KeyPath, TransactionInfo, Wallet, WalletInfo,
 | 
			
		||||
};
 | 
			
		||||
use ethereum_types::{Address, H256, U256};
 | 
			
		||||
use ethkey::Signature;
 | 
			
		||||
use hidapi;
 | 
			
		||||
use libusb;
 | 
			
		||||
use parking_lot::{Mutex, RwLock};
 | 
			
		||||
use protobuf::{self, Message, ProtobufEnum};
 | 
			
		||||
use trezor_sys::messages::{
 | 
			
		||||
    ButtonAck, EthereumAddress, EthereumGetAddress, EthereumSignTx, EthereumTxAck,
 | 
			
		||||
    EthereumTxRequest, MessageType, PinMatrixAck,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Trezor v1 vendor ID
 | 
			
		||||
const TREZOR_VID: u16 = 0x534c;
 | 
			
		||||
/// Trezor product IDs
 | 
			
		||||
const TREZOR_PIDS: [u16; 1] = [0x0001];
 | 
			
		||||
 | 
			
		||||
const ETH_DERIVATION_PATH: [u32; 5] = [0x8000_002C, 0x8000_003C, 0x8000_0000, 0, 0]; // m/44'/60'/0'/0/0
 | 
			
		||||
const ETC_DERIVATION_PATH: [u32; 5] = [0x8000_002C, 0x8000_003D, 0x8000_0000, 0, 0]; // m/44'/61'/0'/0/0
 | 
			
		||||
 | 
			
		||||
/// Hardware wallet error.
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum Error {
 | 
			
		||||
    /// Ethereum wallet protocol error.
 | 
			
		||||
    Protocol(&'static str),
 | 
			
		||||
    /// Hidapi error.
 | 
			
		||||
    Usb(hidapi::HidError),
 | 
			
		||||
    /// Libusb error
 | 
			
		||||
    LibUsb(libusb::Error),
 | 
			
		||||
    /// Device with request key is not available.
 | 
			
		||||
    KeyNotFound,
 | 
			
		||||
    /// Signing has been cancelled by user.
 | 
			
		||||
    UserCancel,
 | 
			
		||||
    /// The Message Type given in the trezor RPC call is not something we recognize
 | 
			
		||||
    BadMessageType,
 | 
			
		||||
    /// Trying to read from a closed device at the given path
 | 
			
		||||
    LockedDevice(String),
 | 
			
		||||
    /// Signing messages are not supported by Trezor
 | 
			
		||||
    NoSigningMessage,
 | 
			
		||||
    /// No device arrived
 | 
			
		||||
    NoDeviceArrived,
 | 
			
		||||
    /// No device left
 | 
			
		||||
    NoDeviceLeft,
 | 
			
		||||
    /// Invalid PID or VID
 | 
			
		||||
    InvalidDevice,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Error {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Error::Protocol(ref s) => write!(f, "Trezor protocol error: {}", s),
 | 
			
		||||
            Error::Usb(ref e) => write!(f, "USB communication error: {}", e),
 | 
			
		||||
            Error::LibUsb(ref e) => write!(f, "LibUSB communication error: {}", e),
 | 
			
		||||
            Error::KeyNotFound => write!(f, "Key not found"),
 | 
			
		||||
            Error::UserCancel => write!(f, "Operation has been cancelled"),
 | 
			
		||||
            Error::BadMessageType => write!(f, "Bad Message Type in RPC call"),
 | 
			
		||||
            Error::LockedDevice(ref s) => write!(
 | 
			
		||||
                f,
 | 
			
		||||
                "Device is locked, needs PIN to perform operations: {}",
 | 
			
		||||
                s
 | 
			
		||||
            ),
 | 
			
		||||
            Error::NoSigningMessage => write!(f, "Signing messages are not supported by Trezor"),
 | 
			
		||||
            Error::NoDeviceArrived => write!(f, "No device arrived"),
 | 
			
		||||
            Error::NoDeviceLeft => write!(f, "No device left"),
 | 
			
		||||
            Error::InvalidDevice => write!(
 | 
			
		||||
                f,
 | 
			
		||||
                "Device with non-supported product ID or vendor ID was detected"
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<hidapi::HidError> for Error {
 | 
			
		||||
    fn from(err: hidapi::HidError) -> Self {
 | 
			
		||||
        Error::Usb(err)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<libusb::Error> for Error {
 | 
			
		||||
    fn from(err: libusb::Error) -> Self {
 | 
			
		||||
        Error::LibUsb(err)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<protobuf::ProtobufError> for Error {
 | 
			
		||||
    fn from(_: protobuf::ProtobufError) -> Self {
 | 
			
		||||
        Error::Protocol(&"Could not read response from Trezor Device")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Trezor device manager
 | 
			
		||||
pub struct Manager {
 | 
			
		||||
    usb: Arc<Mutex<hidapi::HidApi>>,
 | 
			
		||||
    devices: RwLock<Vec<Device>>,
 | 
			
		||||
    locked_devices: RwLock<Vec<String>>,
 | 
			
		||||
    key_path: RwLock<KeyPath>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// HID Version used for the Trezor device
 | 
			
		||||
enum HidVersion {
 | 
			
		||||
    V1,
 | 
			
		||||
    V2,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Manager {
 | 
			
		||||
    /// Create a new instance.
 | 
			
		||||
    pub fn new(usb: Arc<Mutex<hidapi::HidApi>>) -> Arc<Self> {
 | 
			
		||||
        Arc::new(Self {
 | 
			
		||||
            usb,
 | 
			
		||||
            devices: RwLock::new(Vec::new()),
 | 
			
		||||
            locked_devices: RwLock::new(Vec::new()),
 | 
			
		||||
            key_path: RwLock::new(KeyPath::Ethereum),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn pin_matrix_ack(&self, device_path: &str, pin: &str) -> Result<bool, Error> {
 | 
			
		||||
        let unlocked = {
 | 
			
		||||
            let usb = self.usb.lock();
 | 
			
		||||
            let device = self.open_path(|| usb.open_path(&device_path))?;
 | 
			
		||||
            let t = MessageType::MessageType_PinMatrixAck;
 | 
			
		||||
            let mut m = PinMatrixAck::new();
 | 
			
		||||
            m.set_pin(pin.to_string());
 | 
			
		||||
            self.send_device_message(&device, t, &m)?;
 | 
			
		||||
            let (resp_type, _) = self.read_device_response(&device)?;
 | 
			
		||||
            match resp_type {
 | 
			
		||||
                // Getting an Address back means it's unlocked, this is undocumented behavior
 | 
			
		||||
                MessageType::MessageType_EthereumAddress => Ok(true),
 | 
			
		||||
                // Getting anything else means we didn't unlock it
 | 
			
		||||
                _ => Ok(false),
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        self.update_devices(DeviceDirection::Arrived)?;
 | 
			
		||||
        unlocked
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn u256_to_be_vec(&self, val: &U256) -> Vec<u8> {
 | 
			
		||||
        let mut buf = [0_u8; 32];
 | 
			
		||||
        val.to_big_endian(&mut buf);
 | 
			
		||||
        buf.iter().skip_while(|x| **x == 0).cloned().collect()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn signing_loop(
 | 
			
		||||
        &self,
 | 
			
		||||
        handle: &hidapi::HidDevice,
 | 
			
		||||
        chain_id: &Option<u64>,
 | 
			
		||||
        data: &[u8],
 | 
			
		||||
    ) -> Result<Signature, Error> {
 | 
			
		||||
        let (resp_type, bytes) = self.read_device_response(&handle)?;
 | 
			
		||||
        match resp_type {
 | 
			
		||||
            MessageType::MessageType_Cancel => Err(Error::UserCancel),
 | 
			
		||||
            MessageType::MessageType_ButtonRequest => {
 | 
			
		||||
                self.send_device_message(
 | 
			
		||||
                    handle,
 | 
			
		||||
                    MessageType::MessageType_ButtonAck,
 | 
			
		||||
                    &ButtonAck::new(),
 | 
			
		||||
                )?;
 | 
			
		||||
                // Signing loop goes back to the top and reading blocks
 | 
			
		||||
                // for up to 5 minutes waiting for response from the device
 | 
			
		||||
                // if the user doesn't click any button within 5 minutes you
 | 
			
		||||
                // get a signing error and the device sort of locks up on the signing screen
 | 
			
		||||
                self.signing_loop(handle, chain_id, data)
 | 
			
		||||
            }
 | 
			
		||||
            MessageType::MessageType_EthereumTxRequest => {
 | 
			
		||||
                let resp: EthereumTxRequest = protobuf::core::parse_from_bytes(&bytes)?;
 | 
			
		||||
                if resp.has_data_length() {
 | 
			
		||||
                    let mut msg = EthereumTxAck::new();
 | 
			
		||||
                    let len = resp.get_data_length() as usize;
 | 
			
		||||
                    msg.set_data_chunk(data[..len].to_vec());
 | 
			
		||||
                    self.send_device_message(handle, MessageType::MessageType_EthereumTxAck, &msg)?;
 | 
			
		||||
                    self.signing_loop(handle, chain_id, &data[len..])
 | 
			
		||||
                } else {
 | 
			
		||||
                    let v = resp.get_signature_v();
 | 
			
		||||
                    let r = H256::from_slice(resp.get_signature_r());
 | 
			
		||||
                    let s = H256::from_slice(resp.get_signature_s());
 | 
			
		||||
                    if let Some(c_id) = *chain_id {
 | 
			
		||||
                        // If there is a chain_id supplied, Trezor will return a v
 | 
			
		||||
                        // part of the signature that is already adjusted for EIP-155,
 | 
			
		||||
                        // so v' = v + 2 * chain_id + 35, but code further down the
 | 
			
		||||
                        // pipeline will already do this transformation, so remove it here
 | 
			
		||||
                        let adjustment = 35 + 2 * c_id as u32;
 | 
			
		||||
                        Ok(Signature::from_rsv(
 | 
			
		||||
                            &r,
 | 
			
		||||
                            &s,
 | 
			
		||||
                            (max(v, adjustment) - adjustment) as u8,
 | 
			
		||||
                        ))
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // If there isn't a chain_id, v will be returned as v + 27
 | 
			
		||||
                        let adjusted_v = if v < 27 { v } else { v - 27 };
 | 
			
		||||
                        Ok(Signature::from_rsv(&r, &s, adjusted_v as u8))
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            MessageType::MessageType_Failure => {
 | 
			
		||||
                Err(Error::Protocol("Last message sent to Trezor failed"))
 | 
			
		||||
            }
 | 
			
		||||
            _ => Err(Error::Protocol("Unexpected response from Trezor device.")),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn send_device_message(
 | 
			
		||||
        &self,
 | 
			
		||||
        device: &hidapi::HidDevice,
 | 
			
		||||
        msg_type: MessageType,
 | 
			
		||||
        msg: &dyn Message,
 | 
			
		||||
    ) -> Result<usize, Error> {
 | 
			
		||||
        let msg_id = msg_type as u16;
 | 
			
		||||
        let mut message = msg.write_to_bytes()?;
 | 
			
		||||
        let msg_size = message.len();
 | 
			
		||||
        let mut data = Vec::new();
 | 
			
		||||
        let hid_version = self.probe_hid_version(device)?;
 | 
			
		||||
        // Magic constants
 | 
			
		||||
        data.push(b'#');
 | 
			
		||||
        data.push(b'#');
 | 
			
		||||
        // Convert msg_id to BE and split into bytes
 | 
			
		||||
        data.push(((msg_id >> 8) & 0xFF) as u8);
 | 
			
		||||
        data.push((msg_id & 0xFF) as u8);
 | 
			
		||||
        // Convert msg_size to BE and split into bytes
 | 
			
		||||
        data.push(((msg_size >> 24) & 0xFF) as u8);
 | 
			
		||||
        data.push(((msg_size >> 16) & 0xFF) as u8);
 | 
			
		||||
        data.push(((msg_size >> 8) & 0xFF) as u8);
 | 
			
		||||
        data.push((msg_size & 0xFF) as u8);
 | 
			
		||||
        data.append(&mut message);
 | 
			
		||||
        while data.len() % 63 > 0 {
 | 
			
		||||
            data.push(0);
 | 
			
		||||
        }
 | 
			
		||||
        let mut total_written = 0;
 | 
			
		||||
        for chunk in data.chunks(63) {
 | 
			
		||||
            let mut padded_chunk = match hid_version {
 | 
			
		||||
                HidVersion::V1 => vec![b'?'],
 | 
			
		||||
                HidVersion::V2 => vec![0, b'?'],
 | 
			
		||||
            };
 | 
			
		||||
            padded_chunk.extend_from_slice(&chunk);
 | 
			
		||||
            total_written += device.write(&padded_chunk)?;
 | 
			
		||||
        }
 | 
			
		||||
        Ok(total_written)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn probe_hid_version(&self, device: &hidapi::HidDevice) -> Result<HidVersion, Error> {
 | 
			
		||||
        let mut buf2 = [0xFF_u8; 65];
 | 
			
		||||
        buf2[0] = 0;
 | 
			
		||||
        buf2[1] = 63;
 | 
			
		||||
        let mut buf1 = [0xFF_u8; 64];
 | 
			
		||||
        buf1[0] = 63;
 | 
			
		||||
        if device.write(&buf2)? == 65 {
 | 
			
		||||
            Ok(HidVersion::V2)
 | 
			
		||||
        } else if device.write(&buf1)? == 64 {
 | 
			
		||||
            Ok(HidVersion::V1)
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(Error::Usb("Unable to determine HID Version"))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn read_device_response(
 | 
			
		||||
        &self,
 | 
			
		||||
        device: &hidapi::HidDevice,
 | 
			
		||||
    ) -> Result<(MessageType, Vec<u8>), Error> {
 | 
			
		||||
        let protocol_err = Error::Protocol(&"Unexpected wire response from Trezor Device");
 | 
			
		||||
        let mut buf = vec![0; 64];
 | 
			
		||||
 | 
			
		||||
        let first_chunk = device.read_timeout(&mut buf, 300_000)?;
 | 
			
		||||
        if first_chunk < 9 || buf[0] != b'?' || buf[1] != b'#' || buf[2] != b'#' {
 | 
			
		||||
            return Err(protocol_err);
 | 
			
		||||
        }
 | 
			
		||||
        let msg_type =
 | 
			
		||||
            MessageType::from_i32(((buf[3] as i32 & 0xFF) << 8) + (buf[4] as i32 & 0xFF))
 | 
			
		||||
                .ok_or(protocol_err)?;
 | 
			
		||||
        let msg_size = ((buf[5] as u32 & 0xFF) << 24)
 | 
			
		||||
            + ((buf[6] as u32 & 0xFF) << 16)
 | 
			
		||||
            + ((buf[7] as u32 & 0xFF) << 8)
 | 
			
		||||
            + (buf[8] as u32 & 0xFF);
 | 
			
		||||
        let mut data = Vec::new();
 | 
			
		||||
        data.extend_from_slice(&buf[9..]);
 | 
			
		||||
        while data.len() < (msg_size as usize) {
 | 
			
		||||
            device.read_timeout(&mut buf, 10_000)?;
 | 
			
		||||
            data.extend_from_slice(&buf[1..]);
 | 
			
		||||
        }
 | 
			
		||||
        Ok((msg_type, data[..msg_size as usize].to_vec()))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Wallet<'a> for Manager {
 | 
			
		||||
    type Error = Error;
 | 
			
		||||
    type Transaction = &'a TransactionInfo;
 | 
			
		||||
 | 
			
		||||
    fn sign_transaction(
 | 
			
		||||
        &self,
 | 
			
		||||
        address: &Address,
 | 
			
		||||
        t_info: Self::Transaction,
 | 
			
		||||
    ) -> Result<Signature, Error> {
 | 
			
		||||
        let usb = self.usb.lock();
 | 
			
		||||
        let devices = self.devices.read();
 | 
			
		||||
        let device = devices
 | 
			
		||||
            .iter()
 | 
			
		||||
            .find(|d| &d.info.address == address)
 | 
			
		||||
            .ok_or(Error::KeyNotFound)?;
 | 
			
		||||
        let handle = self.open_path(|| usb.open_path(&device.path))?;
 | 
			
		||||
        let msg_type = MessageType::MessageType_EthereumSignTx;
 | 
			
		||||
        let mut message = EthereumSignTx::new();
 | 
			
		||||
        match *self.key_path.read() {
 | 
			
		||||
            KeyPath::Ethereum => message.set_address_n(ETH_DERIVATION_PATH.to_vec()),
 | 
			
		||||
            KeyPath::EthereumClassic => message.set_address_n(ETC_DERIVATION_PATH.to_vec()),
 | 
			
		||||
        }
 | 
			
		||||
        message.set_nonce(self.u256_to_be_vec(&t_info.nonce));
 | 
			
		||||
        message.set_gas_limit(self.u256_to_be_vec(&t_info.gas_limit));
 | 
			
		||||
        message.set_gas_price(self.u256_to_be_vec(&t_info.gas_price));
 | 
			
		||||
        message.set_value(self.u256_to_be_vec(&t_info.value));
 | 
			
		||||
 | 
			
		||||
        if let Some(addr) = t_info.to {
 | 
			
		||||
            message.set_to(addr.to_vec())
 | 
			
		||||
        }
 | 
			
		||||
        let first_chunk_length = min(t_info.data.len(), 1024);
 | 
			
		||||
        let chunk = &t_info.data[0..first_chunk_length];
 | 
			
		||||
        message.set_data_initial_chunk(chunk.to_vec());
 | 
			
		||||
        message.set_data_length(t_info.data.len() as u32);
 | 
			
		||||
        if let Some(c_id) = t_info.chain_id {
 | 
			
		||||
            message.set_chain_id(c_id as u32);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.send_device_message(&handle, msg_type, &message)?;
 | 
			
		||||
 | 
			
		||||
        self.signing_loop(
 | 
			
		||||
            &handle,
 | 
			
		||||
            &t_info.chain_id,
 | 
			
		||||
            &t_info.data[first_chunk_length..],
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn set_key_path(&self, key_path: KeyPath) {
 | 
			
		||||
        *self.key_path.write() = key_path;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn update_devices(&self, device_direction: DeviceDirection) -> Result<usize, Error> {
 | 
			
		||||
        let mut usb = self.usb.lock();
 | 
			
		||||
        usb.refresh_devices();
 | 
			
		||||
        let devices = usb.devices();
 | 
			
		||||
        let num_prev_devices = self.devices.read().len();
 | 
			
		||||
 | 
			
		||||
        let detected_devices = devices
 | 
			
		||||
            .iter()
 | 
			
		||||
            .filter(|&d| {
 | 
			
		||||
                is_valid_trezor(d.vendor_id, d.product_id)
 | 
			
		||||
                    && is_valid_hid_device(d.usage_page, d.interface_number)
 | 
			
		||||
            })
 | 
			
		||||
            .fold(Vec::new(), |mut v, d| {
 | 
			
		||||
                match self.read_device(&usb, &d) {
 | 
			
		||||
                    Ok(info) => {
 | 
			
		||||
                        trace!(target: "hw", "Found device: {:?}", info);
 | 
			
		||||
                        v.push(info);
 | 
			
		||||
                    }
 | 
			
		||||
                    Err(e) => trace!(target: "hw", "Error reading device info: {}", e),
 | 
			
		||||
                };
 | 
			
		||||
                v
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        let num_curr_devices = detected_devices.len();
 | 
			
		||||
        *self.devices.write() = detected_devices;
 | 
			
		||||
 | 
			
		||||
        match device_direction {
 | 
			
		||||
            DeviceDirection::Arrived => {
 | 
			
		||||
                if num_curr_devices > num_prev_devices {
 | 
			
		||||
                    Ok(num_curr_devices - num_prev_devices)
 | 
			
		||||
                } else {
 | 
			
		||||
                    Err(Error::NoDeviceArrived)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            DeviceDirection::Left => {
 | 
			
		||||
                if num_prev_devices > num_curr_devices {
 | 
			
		||||
                    Ok(num_prev_devices - num_curr_devices)
 | 
			
		||||
                } else {
 | 
			
		||||
                    Err(Error::NoDeviceLeft)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn read_device(
 | 
			
		||||
        &self,
 | 
			
		||||
        usb: &hidapi::HidApi,
 | 
			
		||||
        dev_info: &hidapi::HidDeviceInfo,
 | 
			
		||||
    ) -> Result<Device, Error> {
 | 
			
		||||
        let handle = self.open_path(|| usb.open_path(&dev_info.path))?;
 | 
			
		||||
        let manufacturer = dev_info
 | 
			
		||||
            .manufacturer_string
 | 
			
		||||
            .clone()
 | 
			
		||||
            .unwrap_or_else(|| "Unknown".to_owned());
 | 
			
		||||
        let name = dev_info
 | 
			
		||||
            .product_string
 | 
			
		||||
            .clone()
 | 
			
		||||
            .unwrap_or_else(|| "Unknown".to_owned());
 | 
			
		||||
        let serial = dev_info
 | 
			
		||||
            .serial_number
 | 
			
		||||
            .clone()
 | 
			
		||||
            .unwrap_or_else(|| "Unknown".to_owned());
 | 
			
		||||
        match self.get_address(&handle) {
 | 
			
		||||
            Ok(Some(addr)) => Ok(Device {
 | 
			
		||||
                path: dev_info.path.clone(),
 | 
			
		||||
                info: WalletInfo {
 | 
			
		||||
                    name,
 | 
			
		||||
                    manufacturer,
 | 
			
		||||
                    serial,
 | 
			
		||||
                    address: addr,
 | 
			
		||||
                },
 | 
			
		||||
            }),
 | 
			
		||||
            Ok(None) => Err(Error::LockedDevice(dev_info.path.clone())),
 | 
			
		||||
            Err(e) => Err(e),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn list_devices(&self) -> Vec<WalletInfo> {
 | 
			
		||||
        self.devices.read().iter().map(|d| d.info.clone()).collect()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn list_locked_devices(&self) -> Vec<String> {
 | 
			
		||||
        (*self.locked_devices.read()).clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_wallet(&self, address: &Address) -> Option<WalletInfo> {
 | 
			
		||||
        self.devices
 | 
			
		||||
            .read()
 | 
			
		||||
            .iter()
 | 
			
		||||
            .find(|d| &d.info.address == address)
 | 
			
		||||
            .map(|d| d.info.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_address(&self, device: &hidapi::HidDevice) -> Result<Option<Address>, Error> {
 | 
			
		||||
        let typ = MessageType::MessageType_EthereumGetAddress;
 | 
			
		||||
        let mut message = EthereumGetAddress::new();
 | 
			
		||||
        match *self.key_path.read() {
 | 
			
		||||
            KeyPath::Ethereum => message.set_address_n(ETH_DERIVATION_PATH.to_vec()),
 | 
			
		||||
            KeyPath::EthereumClassic => message.set_address_n(ETC_DERIVATION_PATH.to_vec()),
 | 
			
		||||
        }
 | 
			
		||||
        message.set_show_display(false);
 | 
			
		||||
        self.send_device_message(&device, typ, &message)?;
 | 
			
		||||
 | 
			
		||||
        let (resp_type, bytes) = self.read_device_response(&device)?;
 | 
			
		||||
        match resp_type {
 | 
			
		||||
            MessageType::MessageType_EthereumAddress => {
 | 
			
		||||
                let response: EthereumAddress = protobuf::core::parse_from_bytes(&bytes)?;
 | 
			
		||||
                Ok(Some(From::from(response.get_address())))
 | 
			
		||||
            }
 | 
			
		||||
            _ => Ok(None),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn open_path<R, F>(&self, f: F) -> Result<R, Error>
 | 
			
		||||
    where
 | 
			
		||||
        F: Fn() -> Result<R, &'static str>,
 | 
			
		||||
    {
 | 
			
		||||
        f().map_err(Into::into)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Poll the device in maximum `max_polling_duration` if it doesn't succeed
 | 
			
		||||
pub fn try_connect_polling(trezor: &Manager, duration: &Duration, dir: DeviceDirection) -> bool {
 | 
			
		||||
    let start_time = Instant::now();
 | 
			
		||||
    while start_time.elapsed() <= *duration {
 | 
			
		||||
        if let Ok(num_devices) = trezor.update_devices(dir) {
 | 
			
		||||
            trace!(target: "hw", "{} Trezor devices {}", num_devices, dir);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Check if the detected device is a Trezor device by checking both the product ID and the vendor ID
 | 
			
		||||
pub fn is_valid_trezor(vid: u16, pid: u16) -> bool {
 | 
			
		||||
    vid == TREZOR_VID && TREZOR_PIDS.contains(&pid)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
#[ignore]
 | 
			
		||||
/// This test can't be run without an actual trezor device connected
 | 
			
		||||
/// (and unlocked) attached to the machine that's running the test
 | 
			
		||||
fn test_signature() {
 | 
			
		||||
    use super::HardwareWalletManager;
 | 
			
		||||
    use ethereum_types::Address;
 | 
			
		||||
    use MAX_POLLING_DURATION;
 | 
			
		||||
 | 
			
		||||
    let manager = HardwareWalletManager::new().unwrap();
 | 
			
		||||
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        try_connect_polling(
 | 
			
		||||
            &manager.trezor,
 | 
			
		||||
            &MAX_POLLING_DURATION,
 | 
			
		||||
            DeviceDirection::Arrived
 | 
			
		||||
        ),
 | 
			
		||||
        true
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let addr: Address = manager
 | 
			
		||||
        .list_wallets()
 | 
			
		||||
        .iter()
 | 
			
		||||
        .filter(|d| d.name == "TREZOR".to_string() && d.manufacturer == "SatoshiLabs".to_string())
 | 
			
		||||
        .nth(0)
 | 
			
		||||
        .map(|d| d.address)
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    let t_info = TransactionInfo {
 | 
			
		||||
        nonce: U256::from(1),
 | 
			
		||||
        gas_price: U256::from(100),
 | 
			
		||||
        gas_limit: U256::from(21_000),
 | 
			
		||||
        to: Some(Address::from(1337)),
 | 
			
		||||
        chain_id: Some(1),
 | 
			
		||||
        value: U256::from(1_000_000),
 | 
			
		||||
        data: (&[1u8; 3000]).to_vec(),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let signature = manager.trezor.sign_transaction(&addr, &t_info);
 | 
			
		||||
    assert!(signature.is_ok());
 | 
			
		||||
}
 | 
			
		||||
@ -17,7 +17,6 @@
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
use ethstore::Error as SSError;
 | 
			
		||||
use hardware_wallet::Error as HardwareError;
 | 
			
		||||
 | 
			
		||||
/// Signing error
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
@ -26,8 +25,6 @@ pub enum SignError {
 | 
			
		||||
    NotUnlocked,
 | 
			
		||||
    /// Account does not exist.
 | 
			
		||||
    NotFound,
 | 
			
		||||
    /// Low-level hardware device error.
 | 
			
		||||
    Hardware(HardwareError),
 | 
			
		||||
    /// Low-level error from store
 | 
			
		||||
    SStore(SSError),
 | 
			
		||||
}
 | 
			
		||||
@ -37,18 +34,11 @@ impl fmt::Display for SignError {
 | 
			
		||||
        match *self {
 | 
			
		||||
            SignError::NotUnlocked => write!(f, "Account is locked"),
 | 
			
		||||
            SignError::NotFound => write!(f, "Account does not exist"),
 | 
			
		||||
            SignError::Hardware(ref e) => write!(f, "{}", e),
 | 
			
		||||
            SignError::SStore(ref e) => write!(f, "{}", e),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<HardwareError> for SignError {
 | 
			
		||||
    fn from(e: HardwareError) -> Self {
 | 
			
		||||
        SignError::Hardware(e)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<SSError> for SignError {
 | 
			
		||||
    fn from(e: SSError) -> Self {
 | 
			
		||||
        SignError::SStore(e)
 | 
			
		||||
 | 
			
		||||
@ -22,9 +22,6 @@ mod account_data;
 | 
			
		||||
mod error;
 | 
			
		||||
mod stores;
 | 
			
		||||
 | 
			
		||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
 | 
			
		||||
extern crate fake_hardware_wallet as hardware_wallet;
 | 
			
		||||
 | 
			
		||||
use self::{
 | 
			
		||||
    account_data::{AccountData, Unlock},
 | 
			
		||||
    stores::AddressBook,
 | 
			
		||||
@ -35,20 +32,16 @@ use std::{
 | 
			
		||||
    time::{Duration, Instant},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use common_types::transaction::{Action, Transaction};
 | 
			
		||||
use ethkey::{Address, Generator, Message, Password, Public, Random, Secret};
 | 
			
		||||
use ethstore::{
 | 
			
		||||
    accounts_dir::MemoryDirectory, random_string, EthMultiStore, EthStore, OpaqueSecret,
 | 
			
		||||
    SecretStore, SecretVaultRef, SimpleSecretStore, StoreAccountRef,
 | 
			
		||||
};
 | 
			
		||||
use log::{debug, warn};
 | 
			
		||||
use log::*;
 | 
			
		||||
use parking_lot::RwLock;
 | 
			
		||||
 | 
			
		||||
pub use ethkey::Signature;
 | 
			
		||||
pub use ethstore::{Derivation, Error, IndexDerivation, KeyFile};
 | 
			
		||||
pub use hardware_wallet::{
 | 
			
		||||
    Error as HardwareError, HardwareWalletManager, KeyPath, TransactionInfo,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub use self::{account_data::AccountMeta, error::SignError};
 | 
			
		||||
 | 
			
		||||
@ -57,10 +50,6 @@ type AccountToken = Password;
 | 
			
		||||
/// Account management settings.
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct AccountProviderSettings {
 | 
			
		||||
    /// Enable hardware wallet support.
 | 
			
		||||
    pub enable_hardware_wallets: bool,
 | 
			
		||||
    /// Use the classic chain key on the hardware wallet.
 | 
			
		||||
    pub hardware_wallet_classic_key: bool,
 | 
			
		||||
    /// Store raw account secret when unlocking the account permanently.
 | 
			
		||||
    pub unlock_keep_secret: bool,
 | 
			
		||||
    /// Disallowed accounts.
 | 
			
		||||
@ -80,8 +69,6 @@ pub struct AccountProvider {
 | 
			
		||||
    sstore: Box<dyn SecretStore>,
 | 
			
		||||
    /// Accounts unlocked with rolling tokens
 | 
			
		||||
    transient_sstore: EthMultiStore,
 | 
			
		||||
    /// Accounts in hardware wallets.
 | 
			
		||||
    hardware_store: Option<HardwareWalletManager>,
 | 
			
		||||
    /// When unlocking account permanently we additionally keep a raw secret in memory
 | 
			
		||||
    /// to increase the performance of transaction signing.
 | 
			
		||||
    unlock_keep_secret: bool,
 | 
			
		||||
@ -97,22 +84,6 @@ fn transient_sstore() -> EthMultiStore {
 | 
			
		||||
impl AccountProvider {
 | 
			
		||||
    /// Creates new account provider.
 | 
			
		||||
    pub fn new(sstore: Box<dyn SecretStore>, settings: AccountProviderSettings) -> Self {
 | 
			
		||||
        let mut hardware_store = None;
 | 
			
		||||
 | 
			
		||||
        if settings.enable_hardware_wallets {
 | 
			
		||||
            match HardwareWalletManager::new() {
 | 
			
		||||
                Ok(manager) => {
 | 
			
		||||
                    manager.set_key_path(if settings.hardware_wallet_classic_key {
 | 
			
		||||
                        KeyPath::EthereumClassic
 | 
			
		||||
                    } else {
 | 
			
		||||
                        KeyPath::Ethereum
 | 
			
		||||
                    });
 | 
			
		||||
                    hardware_store = Some(manager)
 | 
			
		||||
                }
 | 
			
		||||
                Err(e) => debug!("Error initializing hardware wallets: {}", e),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Ok(accounts) = sstore.accounts() {
 | 
			
		||||
            for account in accounts
 | 
			
		||||
                .into_iter()
 | 
			
		||||
@ -135,7 +106,6 @@ impl AccountProvider {
 | 
			
		||||
            address_book: RwLock::new(address_book),
 | 
			
		||||
            sstore: sstore,
 | 
			
		||||
            transient_sstore: transient_sstore(),
 | 
			
		||||
            hardware_store: hardware_store,
 | 
			
		||||
            unlock_keep_secret: settings.unlock_keep_secret,
 | 
			
		||||
            blacklisted_accounts: settings.blacklisted_accounts,
 | 
			
		||||
        }
 | 
			
		||||
@ -152,7 +122,6 @@ impl AccountProvider {
 | 
			
		||||
                    .expect("MemoryDirectory load always succeeds; qed"),
 | 
			
		||||
            ),
 | 
			
		||||
            transient_sstore: transient_sstore(),
 | 
			
		||||
            hardware_store: None,
 | 
			
		||||
            unlock_keep_secret: false,
 | 
			
		||||
            blacklisted_accounts: vec![],
 | 
			
		||||
        }
 | 
			
		||||
@ -262,44 +231,6 @@ impl AccountProvider {
 | 
			
		||||
        Ok(self.accounts()?.first().cloned().unwrap_or_default())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns addresses of hardware accounts.
 | 
			
		||||
    pub fn hardware_accounts(&self) -> Result<Vec<Address>, Error> {
 | 
			
		||||
        if let Some(accounts) = self.hardware_store.as_ref().map(|h| h.list_wallets()) {
 | 
			
		||||
            if !accounts.is_empty() {
 | 
			
		||||
                return Ok(accounts.into_iter().map(|a| a.address).collect());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Err(Error::Custom(
 | 
			
		||||
            "No hardware wallet accounts were found".into(),
 | 
			
		||||
        ))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get a list of paths to locked hardware wallets
 | 
			
		||||
    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),
 | 
			
		||||
            Some(Err(e)) => Err(SignError::Hardware(e)),
 | 
			
		||||
            Some(Ok(s)) => Ok(s),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Provide a pin to a locked hardware wallet on USB path to unlock it
 | 
			
		||||
    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),
 | 
			
		||||
            Some(Err(e)) => Err(SignError::Hardware(e)),
 | 
			
		||||
            Some(Ok(s)) => Ok(s),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns each address along with metadata.
 | 
			
		||||
    pub fn addresses_info(&self) -> HashMap<Address, AccountMeta> {
 | 
			
		||||
        self.address_book.read().get()
 | 
			
		||||
@ -337,49 +268,14 @@ impl AccountProvider {
 | 
			
		||||
        Ok(r)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns each hardware account along with name and meta.
 | 
			
		||||
    pub fn hardware_accounts_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> {
 | 
			
		||||
        let r = self
 | 
			
		||||
            .hardware_accounts()?
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .map(|address| {
 | 
			
		||||
                (
 | 
			
		||||
                    address.clone(),
 | 
			
		||||
                    self.account_meta(address).ok().unwrap_or_default(),
 | 
			
		||||
                )
 | 
			
		||||
            })
 | 
			
		||||
            .collect();
 | 
			
		||||
        Ok(r)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns each hardware account along with name and meta.
 | 
			
		||||
    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.
 | 
			
		||||
    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 {
 | 
			
		||||
                name: info.name,
 | 
			
		||||
                meta: info.manufacturer,
 | 
			
		||||
                uuid: None,
 | 
			
		||||
            })
 | 
			
		||||
        } else {
 | 
			
		||||
            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
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
        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.
 | 
			
		||||
@ -752,54 +648,6 @@ impl AccountProvider {
 | 
			
		||||
    pub fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> {
 | 
			
		||||
        self.sstore.set_vault_meta(name, meta).map_err(Into::into)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sign message with hardware wallet.
 | 
			
		||||
    pub fn sign_message_with_hardware(
 | 
			
		||||
        &self,
 | 
			
		||||
        address: &Address,
 | 
			
		||||
        message: &[u8],
 | 
			
		||||
    ) -> Result<Signature, SignError> {
 | 
			
		||||
        match self
 | 
			
		||||
            .hardware_store
 | 
			
		||||
            .as_ref()
 | 
			
		||||
            .map(|s| s.sign_message(address, message))
 | 
			
		||||
        {
 | 
			
		||||
            None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound),
 | 
			
		||||
            Some(Err(e)) => Err(From::from(e)),
 | 
			
		||||
            Some(Ok(s)) => Ok(s),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sign transaction with hardware wallet.
 | 
			
		||||
    pub fn sign_transaction_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,
 | 
			
		||||
            gas_price: transaction.gas_price,
 | 
			
		||||
            gas_limit: transaction.gas,
 | 
			
		||||
            to: match transaction.action {
 | 
			
		||||
                Action::Create => None,
 | 
			
		||||
                Action::Call(ref to) => Some(to.clone()),
 | 
			
		||||
            },
 | 
			
		||||
            value: transaction.value,
 | 
			
		||||
            data: transaction.data.to_vec(),
 | 
			
		||||
            chain_id: chain_id,
 | 
			
		||||
        };
 | 
			
		||||
        match self
 | 
			
		||||
            .hardware_store
 | 
			
		||||
            .as_ref()
 | 
			
		||||
            .map(|s| s.sign_transaction(&address, &t_info, rlp_encoded_transaction))
 | 
			
		||||
        {
 | 
			
		||||
            None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound),
 | 
			
		||||
            Some(Err(e)) => Err(From::from(e)),
 | 
			
		||||
            Some(Ok(s)) => Ok(s),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
 | 
			
		||||
@ -59,10 +59,6 @@ mod test_signer {
 | 
			
		||||
            match self.0.sign(self.1, Some(self.2.clone()), hash) {
 | 
			
		||||
                Err(SignError::NotUnlocked) => unreachable!(),
 | 
			
		||||
                Err(SignError::NotFound) => Err(ethkey::Error::InvalidAddress),
 | 
			
		||||
                Err(SignError::Hardware(err)) => {
 | 
			
		||||
                    warn!("Error using hardware wallet for engine: {:?}", err);
 | 
			
		||||
                    Err(ethkey::Error::InvalidSecret)
 | 
			
		||||
                }
 | 
			
		||||
                Err(SignError::SStore(accounts::Error::EthKey(err))) => Err(err),
 | 
			
		||||
                Err(SignError::SStore(accounts::Error::EthKeyCrypto(err))) => {
 | 
			
		||||
                    warn!("Low level crypto error: {:?}", err);
 | 
			
		||||
 | 
			
		||||
@ -102,8 +102,6 @@ mod accounts {
 | 
			
		||||
                .map_err(|e| format!("Could not open keys directory: {}", e))?,
 | 
			
		||||
        );
 | 
			
		||||
        let account_settings = AccountProviderSettings {
 | 
			
		||||
            enable_hardware_wallets: cfg.enable_hardware_wallets,
 | 
			
		||||
            hardware_wallet_classic_key: spec == &SpecType::Classic,
 | 
			
		||||
            unlock_keep_secret: cfg.enable_fast_unlock,
 | 
			
		||||
            blacklisted_accounts: match *spec {
 | 
			
		||||
                SpecType::Morden
 | 
			
		||||
 | 
			
		||||
@ -332,10 +332,6 @@ usage! {
 | 
			
		||||
            "Add SHIFT to all port numbers Parity is listening on. Includes network port and all servers (HTTP JSON-RPC, WebSockets JSON-RPC, IPFS, SecretStore).",
 | 
			
		||||
 | 
			
		||||
        ["Account Options"]
 | 
			
		||||
            FLAG flag_no_hardware_wallets: (bool) = false, or |c: &Config| c.account.as_ref()?.disable_hardware.clone(),
 | 
			
		||||
            "--no-hardware-wallets",
 | 
			
		||||
            "Disables hardware wallet support.",
 | 
			
		||||
 | 
			
		||||
            FLAG flag_fast_unlock: (bool) = false, or |c: &Config| c.account.as_ref()?.fast_unlock.clone(),
 | 
			
		||||
            "--fast-unlock",
 | 
			
		||||
            "Use drastically faster unlocking mode. This setting causes raw secrets to be stored unprotected in memory, so use with care.",
 | 
			
		||||
@ -1191,7 +1187,6 @@ struct Account {
 | 
			
		||||
    password: Option<Vec<String>>,
 | 
			
		||||
    keys_iterations: Option<u32>,
 | 
			
		||||
    refresh_time: Option<u64>,
 | 
			
		||||
    disable_hardware: Option<bool>,
 | 
			
		||||
    fast_unlock: Option<bool>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1719,7 +1714,6 @@ mod tests {
 | 
			
		||||
                arg_password: vec!["~/.safe/password.file".into()],
 | 
			
		||||
                arg_keys_iterations: 10240u32,
 | 
			
		||||
                arg_accounts_refresh: 5u64,
 | 
			
		||||
                flag_no_hardware_wallets: false,
 | 
			
		||||
                flag_fast_unlock: false,
 | 
			
		||||
 | 
			
		||||
                // -- Private Transactions Options
 | 
			
		||||
@ -2009,7 +2003,6 @@ mod tests {
 | 
			
		||||
                    password: Some(vec!["passwdfile path".into()]),
 | 
			
		||||
                    keys_iterations: None,
 | 
			
		||||
                    refresh_time: None,
 | 
			
		||||
                    disable_hardware: None,
 | 
			
		||||
                    fast_unlock: None,
 | 
			
		||||
                }),
 | 
			
		||||
                ui: Some(Ui {
 | 
			
		||||
 | 
			
		||||
@ -616,7 +616,6 @@ impl Configuration {
 | 
			
		||||
                .map(|s| replace_home(&self.directories().base, s))
 | 
			
		||||
                .collect(),
 | 
			
		||||
            unlocked_accounts: to_addresses(&self.args.arg_unlock)?,
 | 
			
		||||
            enable_hardware_wallets: !self.args.flag_no_hardware_wallets,
 | 
			
		||||
            enable_fast_unlock: self.args.flag_fast_unlock,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -242,7 +242,6 @@ pub struct AccountsConfig {
 | 
			
		||||
    pub testnet: bool,
 | 
			
		||||
    pub password_files: Vec<String>,
 | 
			
		||||
    pub unlocked_accounts: Vec<Address>,
 | 
			
		||||
    pub enable_hardware_wallets: bool,
 | 
			
		||||
    pub enable_fast_unlock: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -254,7 +253,6 @@ impl Default for AccountsConfig {
 | 
			
		||||
            testnet: false,
 | 
			
		||||
            password_files: Vec::new(),
 | 
			
		||||
            unlocked_accounts: Vec::new(),
 | 
			
		||||
            enable_hardware_wallets: true,
 | 
			
		||||
            enable_fast_unlock: false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -57,10 +57,6 @@ impl super::Accounts for Signer {
 | 
			
		||||
            data: filled.data,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if self.accounts.is_hardware_address(&filled.from) {
 | 
			
		||||
            return hardware_signature(&*self.accounts, filled.from, t, chain_id).map(WithToken::No);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let hash = t.hash(chain_id);
 | 
			
		||||
        let signature = signature(&*self.accounts, filled.from, hash, password)?;
 | 
			
		||||
 | 
			
		||||
@ -76,24 +72,6 @@ impl super::Accounts for Signer {
 | 
			
		||||
        password: SignWith,
 | 
			
		||||
        hash: SignMessage,
 | 
			
		||||
    ) -> Result<WithToken<Signature>> {
 | 
			
		||||
        if self.accounts.is_hardware_address(&address) {
 | 
			
		||||
            return if let SignMessage::Data(data) = hash {
 | 
			
		||||
                let signature = self
 | 
			
		||||
                    .accounts
 | 
			
		||||
                    .sign_message_with_hardware(&address, &data)
 | 
			
		||||
                    // TODO: is this correct? I guess the `token` is the wallet in this context
 | 
			
		||||
                    .map(WithToken::No)
 | 
			
		||||
                    .map_err(|e| errors::account("Error signing message with hardware_wallet", e));
 | 
			
		||||
 | 
			
		||||
                signature
 | 
			
		||||
            } else {
 | 
			
		||||
                Err(errors::account(
 | 
			
		||||
                    "Error signing message with hardware_wallet",
 | 
			
		||||
                    "Message signing is unsupported",
 | 
			
		||||
                ))
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        match hash {
 | 
			
		||||
            SignMessage::Data(data) => {
 | 
			
		||||
                let hash = eth_data_hash(data);
 | 
			
		||||
@ -109,13 +87,6 @@ impl super::Accounts for Signer {
 | 
			
		||||
        password: SignWith,
 | 
			
		||||
        data: Bytes,
 | 
			
		||||
    ) -> Result<WithToken<Bytes>> {
 | 
			
		||||
        if self.accounts.is_hardware_address(&address) {
 | 
			
		||||
            return Err(errors::unsupported(
 | 
			
		||||
                "Decrypting via hardware wallets is not supported.",
 | 
			
		||||
                None,
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        match password.clone() {
 | 
			
		||||
            SignWith::Nothing => self
 | 
			
		||||
                .accounts
 | 
			
		||||
@ -173,27 +144,3 @@ fn signature(
 | 
			
		||||
        _ => errors::password(e),
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// obtain a hardware signature from the given account.
 | 
			
		||||
fn hardware_signature(
 | 
			
		||||
    accounts: &AccountProvider,
 | 
			
		||||
    address: Address,
 | 
			
		||||
    t: Transaction,
 | 
			
		||||
    chain_id: Option<u64>,
 | 
			
		||||
) -> Result<SignedTransaction> {
 | 
			
		||||
    debug_assert!(accounts.is_hardware_address(&address));
 | 
			
		||||
 | 
			
		||||
    let mut stream = rlp::RlpStream::new();
 | 
			
		||||
    t.rlp_append_unsigned_transaction(&mut stream, chain_id);
 | 
			
		||||
    let signature = accounts
 | 
			
		||||
        .sign_transaction_with_hardware(&address, &t, chain_id, &stream.as_raw())
 | 
			
		||||
        .map_err(|e| {
 | 
			
		||||
            debug!(target: "miner", "Error signing transaction with hardware wallet: {}", e);
 | 
			
		||||
            errors::account("Error signing transaction with hardware wallet", e)
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
    SignedTransaction::new(t.with_signature(signature, chain_id)).map_err(|e| {
 | 
			
		||||
        debug!(target: "miner", "Hardware wallet has produced invalid signature: {}", e);
 | 
			
		||||
        errors::account("Invalid signature generated", e)
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@ use v1::{
 | 
			
		||||
        errors,
 | 
			
		||||
    },
 | 
			
		||||
    traits::{ParityAccounts, ParityAccountsInfo},
 | 
			
		||||
    types::{AccountInfo, Derive, DeriveHash, DeriveHierarchical, ExtAccountInfo, HwAccountInfo},
 | 
			
		||||
    types::{AccountInfo, Derive, DeriveHash, DeriveHierarchical, ExtAccountInfo},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Account management (personal) rpc implementation.
 | 
			
		||||
@ -85,35 +85,6 @@ impl ParityAccountsInfo for ParityAccountsClient {
 | 
			
		||||
            .collect())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn hardware_accounts_info(&self) -> Result<BTreeMap<H160, HwAccountInfo>> {
 | 
			
		||||
        self.deprecation_notice("parity_hardwareAccountsInfo");
 | 
			
		||||
 | 
			
		||||
        let info = self
 | 
			
		||||
            .accounts
 | 
			
		||||
            .hardware_accounts_info()
 | 
			
		||||
            .map_err(|e| errors::account("Could not fetch account info.", e))?;
 | 
			
		||||
        Ok(info
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .map(|(a, v)| {
 | 
			
		||||
                (
 | 
			
		||||
                    H160::from(a),
 | 
			
		||||
                    HwAccountInfo {
 | 
			
		||||
                        name: v.name,
 | 
			
		||||
                        manufacturer: v.meta,
 | 
			
		||||
                    },
 | 
			
		||||
                )
 | 
			
		||||
            })
 | 
			
		||||
            .collect())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn locked_hardware_accounts_info(&self) -> Result<Vec<String>> {
 | 
			
		||||
        self.deprecation_notice("parity_lockedHardwareAccountsInfo");
 | 
			
		||||
 | 
			
		||||
        self.accounts
 | 
			
		||||
            .locked_hardware_accounts()
 | 
			
		||||
            .map_err(|e| errors::account("Error communicating with hardware wallet.", e))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn default_account(&self) -> Result<H160> {
 | 
			
		||||
        self.deprecation_notice("parity_defaultAccount");
 | 
			
		||||
 | 
			
		||||
@ -404,14 +375,6 @@ impl ParityAccounts for ParityAccountsClient {
 | 
			
		||||
            .map(Into::into)
 | 
			
		||||
            .map_err(|e| errors::account("Could not sign message.", e))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn hardware_pin_matrix_ack(&self, path: String, pin: String) -> Result<bool> {
 | 
			
		||||
        self.deprecation_notice("parity_hardwarePinMatrixAck");
 | 
			
		||||
 | 
			
		||||
        self.accounts
 | 
			
		||||
            .hardware_pin_matrix_ack(&path, &pin)
 | 
			
		||||
            .map_err(|e| errors::account("Error communicating with hardware wallet.", e))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn into_vec<A, B>(a: Vec<A>) -> Vec<B>
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ use ethkey::Password;
 | 
			
		||||
use ethstore::KeyFile;
 | 
			
		||||
use jsonrpc_core::Result;
 | 
			
		||||
use jsonrpc_derive::rpc;
 | 
			
		||||
use v1::types::{AccountInfo, DeriveHash, DeriveHierarchical, ExtAccountInfo, HwAccountInfo};
 | 
			
		||||
use v1::types::{AccountInfo, DeriveHash, DeriveHierarchical, ExtAccountInfo};
 | 
			
		||||
 | 
			
		||||
/// Parity-specific read-only accounts rpc interface.
 | 
			
		||||
#[rpc(server)]
 | 
			
		||||
@ -31,14 +31,6 @@ pub trait ParityAccountsInfo {
 | 
			
		||||
    #[rpc(name = "parity_accountsInfo")]
 | 
			
		||||
    fn accounts_info(&self) -> Result<BTreeMap<H160, AccountInfo>>;
 | 
			
		||||
 | 
			
		||||
    /// Returns hardware accounts information.
 | 
			
		||||
    #[rpc(name = "parity_hardwareAccountsInfo")]
 | 
			
		||||
    fn hardware_accounts_info(&self) -> Result<BTreeMap<H160, HwAccountInfo>>;
 | 
			
		||||
 | 
			
		||||
    /// Get a list of paths to locked hardware wallets
 | 
			
		||||
    #[rpc(name = "parity_lockedHardwareAccountsInfo")]
 | 
			
		||||
    fn locked_hardware_accounts_info(&self) -> Result<Vec<String>>;
 | 
			
		||||
 | 
			
		||||
    /// Returns default account for dapp.
 | 
			
		||||
    #[rpc(name = "parity_defaultAccount")]
 | 
			
		||||
    fn default_account(&self) -> Result<H160>;
 | 
			
		||||
@ -162,8 +154,4 @@ pub trait ParityAccounts {
 | 
			
		||||
    /// Sign raw hash with the key corresponding to address and password.
 | 
			
		||||
    #[rpc(name = "parity_signMessage")]
 | 
			
		||||
    fn sign_message(&self, _: H160, _: Password, _: H256) -> Result<H520>;
 | 
			
		||||
 | 
			
		||||
    /// Send a PinMatrixAck to a hardware wallet, unlocking it
 | 
			
		||||
    #[rpc(name = "parity_hardwarePinMatrixAck")]
 | 
			
		||||
    fn hardware_pin_matrix_ack(&self, _: String, _: String) -> Result<bool>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -60,15 +60,6 @@ pub struct ExtAccountInfo {
 | 
			
		||||
    pub uuid: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Hardware wallet information.
 | 
			
		||||
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
 | 
			
		||||
pub struct HwAccountInfo {
 | 
			
		||||
    /// Device name.
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    /// Device manufacturer.
 | 
			
		||||
    pub manufacturer: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// account derived from a signature
 | 
			
		||||
/// as well as information that tells if it is valid for
 | 
			
		||||
/// the current chain
 | 
			
		||||
 | 
			
		||||
@ -49,9 +49,7 @@ mod work;
 | 
			
		||||
pub mod pubsub;
 | 
			
		||||
 | 
			
		||||
pub use self::{
 | 
			
		||||
    account_info::{
 | 
			
		||||
        AccountInfo, EthAccount, ExtAccountInfo, HwAccountInfo, RecoveredAccount, StorageProof,
 | 
			
		||||
    },
 | 
			
		||||
    account_info::{AccountInfo, EthAccount, ExtAccountInfo, RecoveredAccount, StorageProof},
 | 
			
		||||
    block::{Block, BlockTransactions, Header, Rich, RichBlock, RichHeader},
 | 
			
		||||
    block_number::{block_number_to_id, BlockNumber, LightBlockNumber},
 | 
			
		||||
    bytes::Bytes,
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,6 @@ RUN apt-get -y update && \
 | 
			
		||||
		curl make cmake file ca-certificates  \
 | 
			
		||||
		g++ gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \
 | 
			
		||||
		libc6-dev-arm64-cross binutils-aarch64-linux-gnu \
 | 
			
		||||
		libudev-dev libudev-dev:arm64 \
 | 
			
		||||
		&& \
 | 
			
		||||
	apt-get clean
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ RUN apt-get -y update && \
 | 
			
		||||
        apt-get install -y --force-yes --no-install-recommends \
 | 
			
		||||
        curl git make g++ gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf \
 | 
			
		||||
        libc6-dev-armhf-cross wget file ca-certificates \
 | 
			
		||||
        binutils-arm-linux-gnueabihf cmake3 libudev-dev \
 | 
			
		||||
        binutils-arm-linux-gnueabihf cmake3 \
 | 
			
		||||
        && \
 | 
			
		||||
    apt-get clean
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -50,4 +50,4 @@ parts:
 | 
			
		||||
      cp -v ethkey $SNAPCRAFT_PART_INSTALL/usr/bin/ethkey
 | 
			
		||||
      cp -v ethstore $SNAPCRAFT_PART_INSTALL/usr/bin/ethstore
 | 
			
		||||
      cp -v whisper $SNAPCRAFT_PART_INSTALL/usr/bin/whisper
 | 
			
		||||
    stage-packages: [libudev1, libstdc++6, cmake, libdb5.3]
 | 
			
		||||
    stage-packages: [libstdc++6, cmake, libdb5.3]
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user