Assorted improvements for ethstore and ethkey (#6961)
* Testing many passwords for presale wallet. * Add multiple threads. * WiP: ethkey brain wallets recover. * Refactor pre-sale-wallet cracking. * Generate in multiple threads. Brain with prefix. * Validate bain wallet phrase. * Brain wallet recovery. * Self-review fixes. * Fix tests. * More docs. * Bump versions. * Remove cmd_find from borked merge. * Update wasm submodules. * Use threadpool.
This commit is contained in:
parent
d1bf0e0e62
commit
7663451116
77
Cargo.lock
generated
77
Cargo.lock
generated
@ -398,6 +398,11 @@ name = "dtoa"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "edit-distance"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.1.0"
|
||||
@ -488,8 +493,8 @@ dependencies = [
|
||||
"ethcore-stratum 1.9.0",
|
||||
"ethcore-util 1.9.0",
|
||||
"ethjson 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"ethstore 0.1.0",
|
||||
"ethkey 0.3.0",
|
||||
"ethstore 0.2.0",
|
||||
"evm 0.1.0",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hardware-wallet 1.9.0",
|
||||
@ -512,7 +517,7 @@ dependencies = [
|
||||
"migration 0.1.0",
|
||||
"native-contracts 0.1.0",
|
||||
"num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-machine 0.1.0",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"patricia-trie 0.1.0",
|
||||
@ -645,7 +650,7 @@ dependencies = [
|
||||
"ethcore-logger 1.9.0",
|
||||
"ethcore-util 1.9.0",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"ethkey 0.3.0",
|
||||
"igd 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ipnetwork 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"keccak-hash 0.1.0",
|
||||
@ -677,7 +682,7 @@ dependencies = [
|
||||
"ethcore-logger 1.9.0",
|
||||
"ethcore-util 1.9.0",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"ethkey 0.3.0",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-cpupool 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -757,7 +762,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)",
|
||||
"ethcore-bigint 0.2.1",
|
||||
"ethkey 0.2.0",
|
||||
"ethkey 0.3.0",
|
||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"subtle 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tiny-keccak 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -777,12 +782,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethkey"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"edit-distance 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)",
|
||||
"ethcore-bigint 0.2.1",
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -794,24 +802,27 @@ name = "ethkey-cli"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethkey 0.2.0",
|
||||
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethkey 0.3.0",
|
||||
"panic_hook 0.1.0",
|
||||
"parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethstore"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"ethcore-bigint 0.2.1",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"ethkey 0.3.0",
|
||||
"itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-wordlist 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -830,8 +841,10 @@ name = "ethstore-cli"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethstore 0.1.0",
|
||||
"ethstore 0.2.0",
|
||||
"num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"panic_hook 0.1.0",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -850,7 +863,7 @@ dependencies = [
|
||||
"ethcore-light 1.9.0",
|
||||
"ethcore-network 1.9.0",
|
||||
"ethcore-util 1.9.0",
|
||||
"ethkey 0.2.0",
|
||||
"ethkey 0.3.0",
|
||||
"heapsize 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ipnetwork 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"keccak-hash 0.1.0",
|
||||
@ -970,7 +983,7 @@ version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1013,7 +1026,7 @@ name = "hardware-wallet"
|
||||
version = "1.9.0"
|
||||
dependencies = [
|
||||
"ethcore-bigint 0.2.1",
|
||||
"ethkey 0.2.0",
|
||||
"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.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1095,7 +1108,7 @@ dependencies = [
|
||||
"language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1817,7 +1830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.6.2"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1920,7 +1933,7 @@ dependencies = [
|
||||
"ethcore-secretstore 1.0.0",
|
||||
"ethcore-stratum 1.9.0",
|
||||
"ethcore-util 1.9.0",
|
||||
"ethkey 0.2.0",
|
||||
"ethkey 0.3.0",
|
||||
"ethsync 1.9.0",
|
||||
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1936,7 +1949,7 @@ dependencies = [
|
||||
"migration 0.1.0",
|
||||
"node-filter 1.9.0",
|
||||
"node-health 0.1.0",
|
||||
"num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"number_prefix 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"panic_hook 0.1.0",
|
||||
"parity-dapps 1.9.0",
|
||||
@ -2074,7 +2087,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethcore 1.9.0",
|
||||
"ethcore-io 1.9.0",
|
||||
"ethkey 0.2.0",
|
||||
"ethkey 0.3.0",
|
||||
"kvdb 0.1.0",
|
||||
"kvdb-memorydb 0.1.0",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2119,8 +2132,8 @@ dependencies = [
|
||||
"ethcore-util 1.9.0",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethjson 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"ethstore 0.1.0",
|
||||
"ethkey 0.3.0",
|
||||
"ethstore 0.2.0",
|
||||
"ethsync 1.9.0",
|
||||
"fetch 0.1.0",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2274,7 +2287,7 @@ dependencies = [
|
||||
"ethcore-bigint 0.2.1",
|
||||
"ethcore-network 1.9.0",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"ethkey 0.3.0",
|
||||
"hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.9)",
|
||||
"jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.9)",
|
||||
@ -2296,7 +2309,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parity-wordlist"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2570,7 +2583,7 @@ dependencies = [
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -3111,6 +3124,14 @@ dependencies = [
|
||||
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "threadpool"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.38"
|
||||
@ -3571,6 +3592,7 @@ dependencies = [
|
||||
"checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8"
|
||||
"checksum docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b5b93718f8b3e5544fcc914c43de828ca6c6ace23e0332c6080a2977b49787a"
|
||||
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
|
||||
"checksum edit-distance 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6a34f5204fbc13582de418611cf3a7dcdd07c6d312a5b631597ba72c06b9d9c9"
|
||||
"checksum either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18785c1ba806c258137c937e44ada9ee7e69a37e3c72077542cd2f069d78562a"
|
||||
"checksum elastic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "258ff6a9a94f648d0379dbd79110e057edbb53eb85cc237e33eadf8e5a30df85"
|
||||
"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
|
||||
@ -3656,7 +3678,7 @@ dependencies = [
|
||||
"checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01"
|
||||
"checksum num-rational 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "288629c76fac4b33556f4b7ab57ba21ae202da65ba8b77466e6d598e31990790"
|
||||
"checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0"
|
||||
"checksum num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aec53c34f2d0247c5ca5d32cca1478762f301740468ee9ee6dcb7a0dd7a0c584"
|
||||
"checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d"
|
||||
"checksum number_prefix 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "59a14be9c211cb9c602bad35ac99f41e9a84b44d71b8cbd3040e3bd02a214902"
|
||||
"checksum odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "c3df9b730298cea3a1c3faa90b7e2f9df3a9c400d0936d6015e6165734eefcba"
|
||||
"checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c"
|
||||
@ -3670,7 +3692,7 @@ dependencies = [
|
||||
"checksum parity-ui-old-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-9-v1.git)" = "<none>"
|
||||
"checksum parity-ui-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-9-shell.git)" = "<none>"
|
||||
"checksum parity-wasm 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8431a184ad88cfbcd71a792aaca319cc7203a94300c26b8dce2d0df0681ea87d"
|
||||
"checksum parity-wordlist 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81451bfab101d186f8fc4a0aa13cb5539b31b02c4ed96425a0842e2a413daba6"
|
||||
"checksum parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0dec124478845b142f68b446cbee953d14d4b41f1bc0425024417720dce693"
|
||||
"checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e"
|
||||
"checksum parking_lot_core 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4f610cb9664da38e417ea3225f23051f589851999535290e077939838ab7a595"
|
||||
"checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356"
|
||||
@ -3758,6 +3780,7 @@ dependencies = [
|
||||
"checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209"
|
||||
"checksum textwrap 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df8e08afc40ae3459e4838f303e465aa50d823df8d7f83ca88108f6d3afe7edd"
|
||||
"checksum thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14"
|
||||
"checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865"
|
||||
"checksum time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d788d3aa77bc0ef3e9621256885555368b47bd495c13dd2e7413c89f845520"
|
||||
"checksum tiny-keccak 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52d12ad79e4063e0cb0ca5efa202ed7244b6ce4d25f4d3abe410b2a66128292"
|
||||
"checksum tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e85d419699ec4b71bfe35bbc25bb8771e52eff0471a7f75c853ad06e200b4f86"
|
||||
|
@ -1,14 +1,17 @@
|
||||
[package]
|
||||
name = "ethkey"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
rand = "0.3.14"
|
||||
lazy_static = "0.2"
|
||||
tiny-keccak = "1.3"
|
||||
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
|
||||
rustc-hex = "1.0"
|
||||
ethcore-bigint = { path = "../util/bigint" }
|
||||
rust-crypto = "0.2"
|
||||
byteorder = "1.0"
|
||||
edit-distance = "2.0"
|
||||
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
|
||||
ethcore-bigint = { path = "../util/bigint" }
|
||||
lazy_static = "0.2"
|
||||
log = "0.3"
|
||||
parity-wordlist = "1.2"
|
||||
rand = "0.3.14"
|
||||
rust-crypto = "0.2"
|
||||
rustc-hex = "1.0"
|
||||
tiny-keccak = "1.3"
|
||||
|
@ -16,13 +16,13 @@ Ethereum keys generator.
|
||||
Copyright 2016, 2017 Parity Technologies (UK) Ltd
|
||||
|
||||
Usage:
|
||||
ethkey info <secret> [options]
|
||||
ethkey info <secret-or-phrase> [options]
|
||||
ethkey generate random [options]
|
||||
ethkey generate prefix <prefix> <iterations> [options]
|
||||
ethkey generate brain <seed> [options]
|
||||
ethkey generate prefix <prefix> [options]
|
||||
ethkey sign <secret> <message>
|
||||
ethkey verify public <public> <signature> <message>
|
||||
ethkey verify address <address> <signature> <message>
|
||||
ethkey recover <address> <known-phrase>
|
||||
ethkey [-h | --help]
|
||||
|
||||
Options:
|
||||
@ -30,15 +30,15 @@ Options:
|
||||
-s, --secret Display only the secret.
|
||||
-p, --public Display only the public.
|
||||
-a, --address Display only the address.
|
||||
-b, --brain Use parity brain wallet algorithm.
|
||||
|
||||
Commands:
|
||||
info Display public and address of the secret.
|
||||
generate Generates new ethereum key.
|
||||
random Random generation.
|
||||
prefix Random generation, but address must start with a prefix
|
||||
brain Generate new key from string seed.
|
||||
generate random Generates new random ethereum key.
|
||||
generate prefix Random generation, but address must start with a prefix.
|
||||
sign Sign message using secret.
|
||||
verify Verify signer of the signature.
|
||||
recover Try to find brain phrase matching given address from partial phrase.
|
||||
```
|
||||
|
||||
### Examples
|
||||
@ -60,20 +60,22 @@ address: 26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5
|
||||
|
||||
--
|
||||
|
||||
#### `generate brain <seed>`
|
||||
*Generate new brain-wallet keypair using 16384 iterations.*
|
||||
|
||||
- `<seed>` - brain-wallet seed, any string
|
||||
#### `info --brain <phrase>`
|
||||
*Display info about private key generate from brain wallet recovery phrase.*
|
||||
|
||||
- `<phrase>` - Parity recovery phrase, 12 words
|
||||
|
||||
```
|
||||
ethkey generate brain "this is sparta"
|
||||
ethkey info --brain "this is sparta"
|
||||
```
|
||||
|
||||
```
|
||||
secret: 17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55
|
||||
public: 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124
|
||||
address: 26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5
|
||||
The recover phrase was not generated by Parity: The word 'this' does not come from the dictionary.
|
||||
|
||||
secret: aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2
|
||||
public: c4c5398da6843632c123f543d714d2d2277716c11ff612b2a2f23c6bda4d6f0327c31cd58c55a9572c3cc141dade0c32747a13b7ef34c241b26c84adbb28fcf4
|
||||
address: 006e27b6a72e1f34c626762f3c4761547aff1421
|
||||
```
|
||||
|
||||
--
|
||||
@ -93,14 +95,30 @@ address: a8fa5dd30a87bb9e3288d604eb74949c515ab66e
|
||||
|
||||
--
|
||||
|
||||
#### `generate prefix <prefix> <iterations>`
|
||||
#### `generate random --brain`
|
||||
*Generate new keypair with recovery phrase randomly.*
|
||||
|
||||
```
|
||||
ethkey generate random --brain
|
||||
```
|
||||
|
||||
```
|
||||
recovery phrase: thwarting scandal creamer nuzzle asparagus blast crouch trusting anytime elixir frenzied octagon
|
||||
secret: 001ce488d50d2f7579dc190c4655f32918d505cee3de63bddc7101bc91c0c2f0
|
||||
public: 4e19a5fdae82596e1485c69b687c9cc52b5078e5b0668ef3ce8543cd90e712cb00df822489bc1f1dcb3623538a54476c7b3def44e1a51dc174e86448b63f42d0
|
||||
address: 00cf3711cbd3a1512570639280758118ba0b2bcb
|
||||
```
|
||||
|
||||
|
||||
--
|
||||
|
||||
#### `generate prefix <prefix>`
|
||||
*Generate new keypair randomly with address starting with prefix.*
|
||||
|
||||
- `<prefix>` - desired address prefix, 0 - 32 bytes long.
|
||||
- `<iterations>` - maximum number of tries before generation is assumed to be a failure.
|
||||
|
||||
```
|
||||
ethkey generate prefix ff 1000
|
||||
ethkey generate prefix ff
|
||||
```
|
||||
|
||||
```
|
||||
@ -111,6 +129,24 @@ address: fff7e25dff2aa60f61f9d98130c8646a01f31649
|
||||
|
||||
--
|
||||
|
||||
#### `generate prefix --brain <prefix>`
|
||||
*Generate new keypair with recovery phrase randomly with address starting with prefix.*
|
||||
|
||||
- `<prefix>` - desired address prefix, 0 - 32 bytes long.
|
||||
|
||||
```
|
||||
ethkey generate prefix --brain 00cf
|
||||
```
|
||||
|
||||
```
|
||||
recovery phrase: thwarting scandal creamer nuzzle asparagus blast crouch trusting anytime elixir frenzied octagon
|
||||
secret: 001ce488d50d2f7579dc190c4655f32918d505cee3de63bddc7101bc91c0c2f0
|
||||
public: 4e19a5fdae82596e1485c69b687c9cc52b5078e5b0668ef3ce8543cd90e712cb00df822489bc1f1dcb3623538a54476c7b3def44e1a51dc174e86448b63f42d0
|
||||
address: 00cf3711cbd3a1512570639280758118ba0b2bcb
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `sign <secret> <message>`
|
||||
*Sign a message with a secret.*
|
||||
|
||||
@ -159,9 +195,32 @@ ethkey verify address 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6
|
||||
true
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
# Ethcore toolchain
|
||||
*this project is a part of the ethcore toolchain*
|
||||
#### `recover <address> <known-phrase>`
|
||||
*Try to recover an account given expected address and partial (too short or with invalid words) recovery phrase.*
|
||||
|
||||
- `<address>` - ethereum address, 20 bytes long
|
||||
- `<known-phrase>` - known phrase, can be in a form of `thwarting * creamer`
|
||||
|
||||
```
|
||||
RUST_LOG="info" ethkey recover "00cf3711cbd3a1512570639280758118ba0b2bcb" "thwarting scandal creamer nuzzle asparagus blast crouch trusting anytime elixir frenzied octag"
|
||||
```
|
||||
|
||||
```
|
||||
INFO:ethkey::brain_recover: Invalid word 'octag', looking for potential substitutions.
|
||||
INFO:ethkey::brain_recover: Closest words: ["ocean", "octagon", "octane", "outage", "tag", "acting", "acts", "aorta", "cage", "chug"]
|
||||
INFO:ethkey::brain_recover: Starting to test 7776 possible combinations.
|
||||
|
||||
thwarting scandal creamer nuzzle asparagus blast crouch trusting anytime elixir frenzied octagon
|
||||
secret: 001ce488d50d2f7579dc190c4655f32918d505cee3de63bddc7101bc91c0c2f0
|
||||
public: 4e19a5fdae82596e1485c69b687c9cc52b5078e5b0668ef3ce8543cd90e712cb00df822489bc1f1dcb3623538a54476c7b3def44e1a51dc174e86448b63f42d0
|
||||
address: 00cf3711cbd3a1512570639280758118ba0b2bcb
|
||||
```
|
||||
|
||||
|
||||
# Parity toolchain
|
||||
*this project is a part of the parity toolchain*
|
||||
|
||||
- [**ethkey**](https://github.com/paritytech/ethkey) - Ethereum keys generator and signer.
|
||||
- [**ethstore**](https://github.com/paritytech/ethstore) - Ethereum key management.
|
||||
|
@ -4,12 +4,15 @@ version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
docopt = "0.8"
|
||||
env_logger = "0.4"
|
||||
ethkey = { path = "../" }
|
||||
panic_hook = { path = "../../panic_hook" }
|
||||
parity-wordlist="1.2"
|
||||
rustc-hex = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
rustc-hex = "1.0"
|
||||
docopt = "0.8"
|
||||
panic_hook = { path = "../../panic_hook" }
|
||||
threadpool = "1.7"
|
||||
|
||||
[[bin]]
|
||||
name = "ethkey"
|
||||
|
@ -15,32 +15,36 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate docopt;
|
||||
extern crate rustc_hex;
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate env_logger;
|
||||
extern crate ethkey;
|
||||
extern crate panic_hook;
|
||||
extern crate parity_wordlist;
|
||||
extern crate rustc_hex;
|
||||
extern crate serde;
|
||||
extern crate threadpool;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use std::{env, fmt, process};
|
||||
use std::num::ParseIntError;
|
||||
use std::{env, fmt, process, io, sync};
|
||||
|
||||
use docopt::Docopt;
|
||||
use ethkey::{KeyPair, Random, Brain, BrainPrefix, Prefix, Error as EthkeyError, Generator, sign, verify_public, verify_address, brain_recover};
|
||||
use rustc_hex::{ToHex, FromHex, FromHexError};
|
||||
use ethkey::{KeyPair, Random, Brain, Prefix, Error as EthkeyError, Generator, sign, verify_public, verify_address};
|
||||
use std::io;
|
||||
|
||||
pub const USAGE: &'static str = r#"
|
||||
Ethereum keys generator.
|
||||
Copyright 2016, 2017 Parity Technologies (UK) Ltd
|
||||
|
||||
Usage:
|
||||
ethkey info <secret> [options]
|
||||
ethkey info <secret-or-phrase> [options]
|
||||
ethkey generate random [options]
|
||||
ethkey generate prefix <prefix> <iterations> [options]
|
||||
ethkey generate brain <seed> [options]
|
||||
ethkey generate prefix <prefix> [options]
|
||||
ethkey sign <secret> <message>
|
||||
ethkey verify public <public> <signature> <message>
|
||||
ethkey verify address <address> <signature> <message>
|
||||
ethkey recover <address> <known-phrase>
|
||||
ethkey [-h | --help]
|
||||
|
||||
Options:
|
||||
@ -48,15 +52,15 @@ Options:
|
||||
-s, --secret Display only the secret.
|
||||
-p, --public Display only the public.
|
||||
-a, --address Display only the address.
|
||||
-b, --brain Use parity brain wallet algorithm.
|
||||
|
||||
Commands:
|
||||
info Display public and address of the secret.
|
||||
generate Generates new ethereum key.
|
||||
random Random generation.
|
||||
prefix Random generation, but address must start with a prefix
|
||||
brain Generate new key from string seed.
|
||||
generate random Generates new random ethereum key.
|
||||
generate prefix Random generation, but address must start with a prefix.
|
||||
sign Sign message using secret.
|
||||
verify Verify signer of the signature.
|
||||
recover Try to find brain phrase matching given address from partial phrase.
|
||||
"#;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@ -65,15 +69,15 @@ struct Args {
|
||||
cmd_generate: bool,
|
||||
cmd_random: bool,
|
||||
cmd_prefix: bool,
|
||||
cmd_brain: bool,
|
||||
cmd_sign: bool,
|
||||
cmd_verify: bool,
|
||||
cmd_public: bool,
|
||||
cmd_address: bool,
|
||||
cmd_recover: bool,
|
||||
arg_prefix: String,
|
||||
arg_iterations: String,
|
||||
arg_seed: String,
|
||||
arg_secret: String,
|
||||
arg_secret_or_phrase: String,
|
||||
arg_known_phrase: String,
|
||||
arg_message: String,
|
||||
arg_public: String,
|
||||
arg_address: String,
|
||||
@ -81,6 +85,7 @@ struct Args {
|
||||
flag_secret: bool,
|
||||
flag_public: bool,
|
||||
flag_address: bool,
|
||||
flag_brain: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -157,6 +162,7 @@ impl DisplayMode {
|
||||
|
||||
fn main() {
|
||||
panic_hook::set();
|
||||
env_logger::init().expect("Logger initialized only once.");
|
||||
|
||||
match execute(env::args()) {
|
||||
Ok(ok) => println!("{}", ok),
|
||||
@ -167,9 +173,13 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
fn display(keypair: KeyPair, mode: DisplayMode) -> String {
|
||||
fn display(result: (KeyPair, Option<String>), mode: DisplayMode) -> String {
|
||||
let keypair = result.0;
|
||||
match mode {
|
||||
DisplayMode::KeyPair => format!("{}", keypair),
|
||||
DisplayMode::KeyPair => match result.1 {
|
||||
Some(extra_data) => format!("{}\n{}", extra_data, keypair),
|
||||
None => format!("{}", keypair)
|
||||
},
|
||||
DisplayMode::Secret => format!("{}", keypair.secret().to_hex()),
|
||||
DisplayMode::Public => format!("{:?}", keypair.public()),
|
||||
DisplayMode::Address => format!("{:?}", keypair.address()),
|
||||
@ -182,23 +192,53 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
|
||||
|
||||
return if args.cmd_info {
|
||||
let display_mode = DisplayMode::new(&args);
|
||||
let secret = args.arg_secret.parse().map_err(|_| EthkeyError::InvalidSecret)?;
|
||||
let keypair = KeyPair::from_secret(secret)?;
|
||||
Ok(display(keypair, display_mode))
|
||||
|
||||
let result = if args.flag_brain {
|
||||
let phrase = args.arg_secret_or_phrase;
|
||||
let phrase_info = validate_phrase(&phrase);
|
||||
let keypair = Brain::new(phrase).generate().expect("Brain wallet generator is infallible; qed");
|
||||
(keypair, Some(phrase_info))
|
||||
} else {
|
||||
let secret = args.arg_secret_or_phrase.parse().map_err(|_| EthkeyError::InvalidSecret)?;
|
||||
(KeyPair::from_secret(secret)?, None)
|
||||
};
|
||||
Ok(display(result, display_mode))
|
||||
} else if args.cmd_generate {
|
||||
let display_mode = DisplayMode::new(&args);
|
||||
let keypair = if args.cmd_random {
|
||||
Random.generate()?
|
||||
let result = if args.cmd_random {
|
||||
if args.flag_brain {
|
||||
let mut brain = BrainPrefix::new(vec![0], usize::max_value(), BRAIN_WORDS);
|
||||
let keypair = brain.generate()?;
|
||||
let phrase = format!("recovery phrase: {}", brain.phrase());
|
||||
(keypair, Some(phrase))
|
||||
} else {
|
||||
(Random.generate()?, None)
|
||||
}
|
||||
} else if args.cmd_prefix {
|
||||
let prefix = args.arg_prefix.from_hex()?;
|
||||
let iterations = usize::from_str_radix(&args.arg_iterations, 10)?;
|
||||
Prefix::new(prefix, iterations).generate()?
|
||||
} else if args.cmd_brain {
|
||||
Brain::new(args.arg_seed).generate().expect("Brain wallet generator is infallible; qed")
|
||||
let brain = args.flag_brain;
|
||||
in_threads(move || {
|
||||
let iterations = 1024;
|
||||
let prefix = prefix.clone();
|
||||
move || {
|
||||
let prefix = prefix.clone();
|
||||
let res = if brain {
|
||||
let mut brain = BrainPrefix::new(prefix, iterations, BRAIN_WORDS);
|
||||
let result = brain.generate();
|
||||
let phrase = format!("recovery phrase: {}", brain.phrase());
|
||||
result.map(|keypair| (keypair, Some(phrase)))
|
||||
} else {
|
||||
unreachable!();
|
||||
let result = Prefix::new(prefix, iterations).generate();
|
||||
result.map(|res| (res, None))
|
||||
};
|
||||
Ok(display(keypair, display_mode))
|
||||
|
||||
Ok(res.map(Some).unwrap_or(None))
|
||||
}
|
||||
})?
|
||||
} else {
|
||||
return Ok(format!("{}", USAGE))
|
||||
};
|
||||
Ok(display(result, display_mode))
|
||||
} else if args.cmd_sign {
|
||||
let secret = args.arg_secret.parse().map_err(|_| EthkeyError::InvalidSecret)?;
|
||||
let message = args.arg_message.parse().map_err(|_| EthkeyError::InvalidMessage)?;
|
||||
@ -214,12 +254,87 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
|
||||
let address = args.arg_address.parse().map_err(|_| EthkeyError::InvalidAddress)?;
|
||||
verify_address(&address, &signature, &message)?
|
||||
} else {
|
||||
unreachable!();
|
||||
return Ok(format!("{}", USAGE))
|
||||
};
|
||||
Ok(format!("{}", ok))
|
||||
} else {
|
||||
unreachable!();
|
||||
} else if args.cmd_recover {
|
||||
let display_mode = DisplayMode::new(&args);
|
||||
let known_phrase = args.arg_known_phrase;
|
||||
let address = args.arg_address.parse().map_err(|_| EthkeyError::InvalidAddress)?;
|
||||
let (phrase, keypair) = in_threads(move || {
|
||||
let mut it = brain_recover::PhrasesIterator::from_known_phrase(&known_phrase, BRAIN_WORDS);
|
||||
move || {
|
||||
let mut i = 0;
|
||||
while let Some(phrase) = it.next() {
|
||||
i += 1;
|
||||
|
||||
let keypair = Brain::new(phrase.clone()).generate().unwrap();
|
||||
if keypair.address() == address {
|
||||
return Ok(Some((phrase, keypair)))
|
||||
}
|
||||
|
||||
if i >= 1024 {
|
||||
return Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
Err(EthkeyError::Custom("Couldn't find any results.".into()))
|
||||
}
|
||||
})?;
|
||||
Ok(display((keypair, Some(phrase)), display_mode))
|
||||
} else {
|
||||
Ok(format!("{}", USAGE))
|
||||
}
|
||||
}
|
||||
|
||||
const BRAIN_WORDS: usize = 12;
|
||||
|
||||
fn validate_phrase(phrase: &str) -> String {
|
||||
match Brain::validate_phrase(phrase, BRAIN_WORDS) {
|
||||
Ok(()) => format!("The recovery phrase looks correct.\n"),
|
||||
Err(err) => format!("The recover phrase was not generated by Parity: {}", err)
|
||||
}
|
||||
}
|
||||
|
||||
fn in_threads<F, X, O>(prepare: F) -> Result<O, EthkeyError> where
|
||||
O: Send + 'static,
|
||||
X: Send + 'static,
|
||||
F: Fn() -> X,
|
||||
X: FnMut() -> Result<Option<O>, EthkeyError>,
|
||||
{
|
||||
let pool = threadpool::Builder::new().build();
|
||||
|
||||
let (tx, rx) = sync::mpsc::sync_channel(1);
|
||||
let is_done = sync::Arc::new(sync::atomic::AtomicBool::default());
|
||||
|
||||
for _ in 0..pool.max_count() {
|
||||
let is_done = is_done.clone();
|
||||
let tx = tx.clone();
|
||||
let mut task = prepare();
|
||||
pool.execute(move || {
|
||||
loop {
|
||||
if is_done.load(sync::atomic::Ordering::SeqCst) {
|
||||
return;
|
||||
}
|
||||
|
||||
let res = match task() {
|
||||
Ok(None) => continue,
|
||||
Ok(Some(v)) => Ok(v),
|
||||
Err(err) => Err(err),
|
||||
};
|
||||
|
||||
// We are interested only in the first response.
|
||||
let _ = tx.send(res);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if let Ok(solution) = rx.recv() {
|
||||
is_done.store(true, sync::atomic::Ordering::SeqCst);
|
||||
return solution;
|
||||
}
|
||||
|
||||
Err(EthkeyError::Custom("No results found.".into()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -242,13 +357,15 @@ address: 26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5".to_owned();
|
||||
|
||||
#[test]
|
||||
fn brain() {
|
||||
let command = vec!["ethkey", "generate", "brain", "this is sparta"]
|
||||
let command = vec!["ethkey", "info", "--brain", "this is sparta"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected =
|
||||
"secret: aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2
|
||||
"The recover phrase was not generated by Parity: The word 'this' does not come from the dictionary.
|
||||
|
||||
secret: aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2
|
||||
public: c4c5398da6843632c123f543d714d2d2277716c11ff612b2a2f23c6bda4d6f0327c31cd58c55a9572c3cc141dade0c32747a13b7ef34c241b26c84adbb28fcf4
|
||||
address: 006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
@ -256,7 +373,7 @@ address: 006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
|
||||
|
||||
#[test]
|
||||
fn secret() {
|
||||
let command = vec!["ethkey", "generate", "brain", "this is sparta", "--secret"]
|
||||
let command = vec!["ethkey", "info", "--brain", "this is sparta", "--secret"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
@ -267,7 +384,7 @@ address: 006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
|
||||
|
||||
#[test]
|
||||
fn public() {
|
||||
let command = vec!["ethkey", "generate", "brain", "this is sparta", "--public"]
|
||||
let command = vec!["ethkey", "info", "--brain", "this is sparta", "--public"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
@ -278,7 +395,7 @@ address: 006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
|
||||
|
||||
#[test]
|
||||
fn address() {
|
||||
let command = vec!["ethkey", "generate", "brain", "this is sparta", "--address"]
|
||||
let command = vec!["ethkey", "info", "-b", "this is sparta", "--address"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
use keccak::Keccak256;
|
||||
use super::{KeyPair, Generator, Secret};
|
||||
use parity_wordlist;
|
||||
|
||||
/// Simple brainwallet.
|
||||
pub struct Brain(String);
|
||||
@ -24,13 +25,17 @@ impl Brain {
|
||||
pub fn new(s: String) -> Self {
|
||||
Brain(s)
|
||||
}
|
||||
|
||||
pub fn validate_phrase(phrase: &str, expected_words: usize) -> Result<(), ::WordlistError> {
|
||||
parity_wordlist::validate_phrase(phrase, expected_words)
|
||||
}
|
||||
}
|
||||
|
||||
impl Generator for Brain {
|
||||
type Error = ::Void;
|
||||
|
||||
fn generate(self) -> Result<KeyPair, Self::Error> {
|
||||
let seed = self.0;
|
||||
fn generate(&mut self) -> Result<KeyPair, Self::Error> {
|
||||
let seed = self.0.clone();
|
||||
let mut secret = seed.into_bytes().keccak256();
|
||||
|
||||
let mut i = 0;
|
||||
@ -43,7 +48,10 @@ impl Generator for Brain {
|
||||
if let Ok(pair) = Secret::from_unsafe_slice(&secret)
|
||||
.and_then(KeyPair::from_secret)
|
||||
{
|
||||
if pair.address()[0] == 0 { return Ok(pair) }
|
||||
if pair.address()[0] == 0 {
|
||||
trace!("Testing: {}, got: {:?}", self.0, pair.address());
|
||||
return Ok(pair)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
70
ethkey/src/brain_prefix.rs
Normal file
70
ethkey/src/brain_prefix.rs
Normal file
@ -0,0 +1,70 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::{Generator, KeyPair, Error, Brain};
|
||||
use parity_wordlist as wordlist;
|
||||
|
||||
/// Tries to find brain-seed keypair with address starting with given prefix.
|
||||
pub struct BrainPrefix {
|
||||
prefix: Vec<u8>,
|
||||
iterations: usize,
|
||||
no_of_words: usize,
|
||||
last_phrase: String,
|
||||
}
|
||||
|
||||
impl BrainPrefix {
|
||||
pub fn new(prefix: Vec<u8>, iterations: usize, no_of_words: usize) -> Self {
|
||||
BrainPrefix {
|
||||
prefix,
|
||||
iterations,
|
||||
no_of_words,
|
||||
last_phrase: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn phrase(&self) -> &str {
|
||||
&self.last_phrase
|
||||
}
|
||||
}
|
||||
|
||||
impl Generator for BrainPrefix {
|
||||
type Error = Error;
|
||||
|
||||
fn generate(&mut self) -> Result<KeyPair, Error> {
|
||||
for _ in 0..self.iterations {
|
||||
let phrase = wordlist::random_phrase(self.no_of_words);
|
||||
let keypair = Brain::new(phrase.clone()).generate().unwrap();
|
||||
if keypair.address().starts_with(&self.prefix) {
|
||||
self.last_phrase = phrase;
|
||||
return Ok(keypair)
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::Custom("Could not find keypair".into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use {Generator, BrainPrefix};
|
||||
|
||||
#[test]
|
||||
fn prefix_generator() {
|
||||
let prefix = vec![0x00u8];
|
||||
let keypair = BrainPrefix::new(prefix.clone(), usize::max_value(), 12).generate().unwrap();
|
||||
assert!(keypair.address().starts_with(&prefix));
|
||||
}
|
||||
}
|
175
ethkey/src/brain_recover.rs
Normal file
175
ethkey/src/brain_recover.rs
Normal file
@ -0,0 +1,175 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use edit_distance::edit_distance;
|
||||
use parity_wordlist;
|
||||
|
||||
use super::{Address, Brain, Generator};
|
||||
|
||||
|
||||
/// Tries to find a phrase for address, given the number
|
||||
/// of expected words and a partial phrase.
|
||||
///
|
||||
/// Returns `None` if phrase couldn't be found.
|
||||
pub fn brain_recover(
|
||||
address: &Address,
|
||||
known_phrase: &str,
|
||||
expected_words: usize,
|
||||
) -> Option<String> {
|
||||
let it = PhrasesIterator::from_known_phrase(known_phrase, expected_words);
|
||||
for phrase in it {
|
||||
let keypair = Brain::new(phrase.clone()).generate().expect("Brain wallets are infallible; qed");
|
||||
trace!("Testing: {}, got: {:?}", phrase, keypair.address());
|
||||
if &keypair.address() == address {
|
||||
return Some(phrase);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn generate_substitutions(word: &str) -> Vec<&'static str> {
|
||||
let mut words = parity_wordlist::WORDS.iter().cloned()
|
||||
.map(|w| (edit_distance(w, word), w))
|
||||
.collect::<Vec<_>>();
|
||||
words.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
|
||||
words.into_iter()
|
||||
.map(|pair| pair.1)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Iterator over possible
|
||||
pub struct PhrasesIterator {
|
||||
words: Vec<Vec<&'static str>>,
|
||||
combinations: u64,
|
||||
indexes: Vec<usize>,
|
||||
has_next: bool,
|
||||
}
|
||||
|
||||
impl PhrasesIterator {
|
||||
pub fn from_known_phrase(known_phrase: &str, expected_words: usize) -> Self {
|
||||
let known_words = parity_wordlist::WORDS.iter().cloned().collect::<HashSet<_>>();
|
||||
let mut words = known_phrase.split(' ')
|
||||
.map(|word| match known_words.get(word) {
|
||||
None => {
|
||||
info!("Invalid word '{}', looking for potential substitutions.", word);
|
||||
let substitutions = generate_substitutions(word);
|
||||
info!("Closest words: {:?}", &substitutions[..10]);
|
||||
substitutions
|
||||
},
|
||||
Some(word) => vec![*word],
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// add missing words
|
||||
if words.len() < expected_words {
|
||||
let to_add = expected_words - words.len();
|
||||
info!("Number of words is insuficcient adding {} more.", to_add);
|
||||
for _ in 0..to_add {
|
||||
words.push(parity_wordlist::WORDS.iter().cloned().collect());
|
||||
}
|
||||
}
|
||||
|
||||
// start searching
|
||||
PhrasesIterator::new(words)
|
||||
}
|
||||
|
||||
pub fn new(words: Vec<Vec<&'static str>>) -> Self {
|
||||
let combinations = words.iter().fold(1u64, |acc, x| acc * x.len() as u64);
|
||||
let indexes = words.iter().map(|_| 0).collect();
|
||||
info!("Starting to test {} possible combinations.", combinations);
|
||||
|
||||
PhrasesIterator {
|
||||
words,
|
||||
combinations,
|
||||
indexes,
|
||||
has_next: combinations > 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn combinations(&self) -> u64 {
|
||||
self.combinations
|
||||
}
|
||||
|
||||
fn current(&self) -> String {
|
||||
let mut s = self.words[0][self.indexes[0]].to_owned();
|
||||
for i in 1..self.indexes.len() {
|
||||
s.push(' ');
|
||||
s.push_str(self.words[i][self.indexes[i]]);
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
fn next_index(&mut self) -> bool {
|
||||
let mut pos = self.indexes.len();
|
||||
while pos > 0 {
|
||||
pos -= 1;
|
||||
self.indexes[pos] += 1;
|
||||
if self.indexes[pos] >= self.words[pos].len() {
|
||||
self.indexes[pos] = 0;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for PhrasesIterator {
|
||||
type Item = String;
|
||||
|
||||
fn next(&mut self) -> Option<String> {
|
||||
if !self.has_next {
|
||||
return None;
|
||||
}
|
||||
|
||||
let phrase = self.current();
|
||||
self.has_next = self.next_index();
|
||||
Some(phrase)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::PhrasesIterator;
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_generate_possible_combinations() {
|
||||
let mut it = PhrasesIterator::new(vec![
|
||||
vec!["1", "2", "3"],
|
||||
vec!["test"],
|
||||
vec!["a", "b", "c"],
|
||||
]);
|
||||
|
||||
assert_eq!(it.combinations(), 9);
|
||||
assert_eq!(it.next(), Some("1 test a".to_owned()));
|
||||
assert_eq!(it.next(), Some("1 test b".to_owned()));
|
||||
assert_eq!(it.next(), Some("1 test c".to_owned()));
|
||||
assert_eq!(it.next(), Some("2 test a".to_owned()));
|
||||
assert_eq!(it.next(), Some("2 test b".to_owned()));
|
||||
assert_eq!(it.next(), Some("2 test c".to_owned()));
|
||||
assert_eq!(it.next(), Some("3 test a".to_owned()));
|
||||
assert_eq!(it.next(), Some("3 test b".to_owned()));
|
||||
assert_eq!(it.next(), Some("3 test c".to_owned()));
|
||||
assert_eq!(it.next(), None);
|
||||
}
|
||||
|
||||
}
|
@ -14,18 +14,25 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate rand;
|
||||
extern crate tiny_keccak;
|
||||
extern crate secp256k1;
|
||||
extern crate rustc_hex;
|
||||
extern crate ethcore_bigint as bigint;
|
||||
extern crate crypto as rcrypto;
|
||||
// #![warn(missing_docs)]
|
||||
|
||||
extern crate byteorder;
|
||||
extern crate crypto as rcrypto;
|
||||
extern crate edit_distance;
|
||||
extern crate ethcore_bigint as bigint;
|
||||
extern crate parity_wordlist;
|
||||
extern crate rand;
|
||||
extern crate rustc_hex;
|
||||
extern crate secp256k1;
|
||||
extern crate tiny_keccak;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
mod brain;
|
||||
mod brain_prefix;
|
||||
mod error;
|
||||
mod keypair;
|
||||
mod keccak;
|
||||
@ -35,9 +42,12 @@ mod signature;
|
||||
mod secret;
|
||||
mod extended;
|
||||
|
||||
pub mod brain_recover;
|
||||
pub mod math;
|
||||
|
||||
pub use self::parity_wordlist::Error as WordlistError;
|
||||
pub use self::brain::Brain;
|
||||
pub use self::brain_prefix::BrainPrefix;
|
||||
pub use self::error::Error;
|
||||
pub use self::keypair::{KeyPair, public_to_address};
|
||||
pub use self::math::public_is_valid;
|
||||
@ -62,7 +72,7 @@ pub trait Generator {
|
||||
type Error;
|
||||
|
||||
/// Should be called to generate new keypair.
|
||||
fn generate(self) -> Result<KeyPair, Self::Error>;
|
||||
fn generate(&mut self) -> Result<KeyPair, Self::Error>;
|
||||
}
|
||||
|
||||
pub type Address = H160;
|
||||
|
@ -34,7 +34,7 @@ impl Prefix {
|
||||
impl Generator for Prefix {
|
||||
type Error = Error;
|
||||
|
||||
fn generate(self) -> Result<KeyPair, Error> {
|
||||
fn generate(&mut self) -> Result<KeyPair, Error> {
|
||||
for _ in 0..self.iterations {
|
||||
let keypair = Random.generate()?;
|
||||
if keypair.address().starts_with(&self.prefix) {
|
||||
|
@ -23,7 +23,7 @@ pub struct Random;
|
||||
impl Generator for Random {
|
||||
type Error = ::std::io::Error;
|
||||
|
||||
fn generate(self) -> Result<KeyPair, Self::Error> {
|
||||
fn generate(&mut self) -> Result<KeyPair, Self::Error> {
|
||||
let mut rng = OsRng::new()?;
|
||||
match rng.generate() {
|
||||
Ok(pair) => Ok(pair),
|
||||
@ -32,10 +32,10 @@ impl Generator for Random {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Generator for &'a mut OsRng {
|
||||
impl Generator for OsRng {
|
||||
type Error = ::Void;
|
||||
|
||||
fn generate(self) -> Result<KeyPair, Self::Error> {
|
||||
fn generate(&mut self) -> Result<KeyPair, Self::Error> {
|
||||
let (sec, publ) = SECP256K1.generate_keypair(self)
|
||||
.expect("context always created with full capabilities; qed");
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ethstore"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
|
@ -21,6 +21,7 @@ Usage:
|
||||
ethstore list [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore import [--src DIR] [--dir DIR]
|
||||
ethstore import-wallet <path> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore find-wallet-pass <path> <password>
|
||||
ethstore remove <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore sign <address> <password> <message> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore public <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
@ -50,6 +51,7 @@ Commands:
|
||||
list List accounts.
|
||||
import Import accounts from src.
|
||||
import-wallet Import presale wallet.
|
||||
find-wallet-pass Tries to open a wallet with list of passwords given.
|
||||
remove Remove account.
|
||||
sign Sign message.
|
||||
public Displays public key for an address.
|
||||
@ -164,6 +166,25 @@ ethstore import-wallet ethwallet.json password.txt
|
||||
e6a3d25a7cb7cd21cb720df5b5e8afd154af1bbb
|
||||
```
|
||||
|
||||
|
||||
--
|
||||
|
||||
#### `find-wallet-pass <path> <password>`
|
||||
Try to open presale wallet given a list of passwords from a file.
|
||||
The list of passwords can be generated using e.g. [Phildo/brutedist](https://github.com/Phildo/brutedist).
|
||||
|
||||
- `<path>` - presale wallet path
|
||||
- `<password>` - possible passwords, file path
|
||||
|
||||
```
|
||||
ethstore find-wallet-pass ethwallet.json passwords.txt
|
||||
```
|
||||
|
||||
```
|
||||
Found password: test
|
||||
```
|
||||
|
||||
|
||||
--
|
||||
|
||||
#### `remove <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]`
|
||||
@ -318,8 +339,8 @@ OK
|
||||
|
||||
--
|
||||
|
||||
# Ethcore toolchain
|
||||
*this project is a part of the ethcore toolchain*
|
||||
# Parity toolchain
|
||||
*this project is a part of the parity toolchain*
|
||||
|
||||
- [**ethkey**](https://github.com/paritytech/ethkey) - Ethereum keys generator and signer.
|
||||
- [**ethstore**](https://github.com/paritytech/ethstore) - Ethereum key management.
|
||||
|
@ -4,10 +4,12 @@ version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
docopt = "0.8"
|
||||
num_cpus = "1.6"
|
||||
rustc-hex = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
docopt = "0.8"
|
||||
parking_lot = "0.4"
|
||||
ethstore = { path = "../" }
|
||||
panic_hook = { path = "../../panic_hook" }
|
||||
|
||||
|
50
ethstore/cli/src/crack.rs
Normal file
50
ethstore/cli/src/crack.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use std::{cmp, thread};
|
||||
use std::sync::Arc;
|
||||
use std::collections::VecDeque;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use ethstore::{PresaleWallet, Error};
|
||||
use num_cpus;
|
||||
|
||||
pub fn run(passwords: VecDeque<String>, wallet_path: &str) -> Result<(), Error> {
|
||||
let passwords = Arc::new(Mutex::new(passwords));
|
||||
|
||||
let mut handles = Vec::new();
|
||||
|
||||
for _ in 0..num_cpus::get() {
|
||||
let passwords = passwords.clone();
|
||||
let wallet = PresaleWallet::open(&wallet_path)?;
|
||||
handles.push(thread::spawn(move || {
|
||||
look_for_password(passwords, wallet);
|
||||
}));
|
||||
}
|
||||
|
||||
for handle in handles {
|
||||
handle.join().map_err(|err| Error::Custom(format!("Error finishing thread: {:?}", err)))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn look_for_password(passwords: Arc<Mutex<VecDeque<String>>>, wallet: PresaleWallet) {
|
||||
let mut counter = 0;
|
||||
while !passwords.lock().is_empty() {
|
||||
let package = {
|
||||
let mut passwords = passwords.lock();
|
||||
let len = passwords.len();
|
||||
passwords.split_off(cmp::min(len, 32))
|
||||
};
|
||||
for pass in package {
|
||||
counter += 1;
|
||||
match wallet.decrypt(&pass) {
|
||||
Ok(_) => {
|
||||
println!("Found password: {}", &pass);
|
||||
passwords.lock().clear();
|
||||
return;
|
||||
},
|
||||
_ if counter % 100 == 0 => print!("."),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -14,21 +14,27 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate rustc_hex;
|
||||
extern crate docopt;
|
||||
extern crate ethstore;
|
||||
extern crate num_cpus;
|
||||
extern crate panic_hook;
|
||||
extern crate parking_lot;
|
||||
extern crate rustc_hex;
|
||||
extern crate serde;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate ethstore;
|
||||
extern crate panic_hook;
|
||||
|
||||
use std::{env, process, fs, fmt};
|
||||
use std::collections::VecDeque;
|
||||
use std::io::Read;
|
||||
use std::{env, process, fs, fmt};
|
||||
|
||||
use docopt::Docopt;
|
||||
use ethstore::ethkey::Address;
|
||||
use ethstore::dir::{paths, KeyDirectory, RootDiskDirectory};
|
||||
use ethstore::{EthStore, SimpleSecretStore, SecretStore, import_accounts, PresaleWallet,
|
||||
SecretVaultRef, StoreAccountRef};
|
||||
use ethstore::ethkey::Address;
|
||||
use ethstore::{EthStore, SimpleSecretStore, SecretStore, import_accounts, PresaleWallet, SecretVaultRef, StoreAccountRef};
|
||||
|
||||
mod crack;
|
||||
|
||||
pub const USAGE: &'static str = r#"
|
||||
Ethereum key management.
|
||||
@ -40,6 +46,7 @@ Usage:
|
||||
ethstore list [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore import [--src DIR] [--dir DIR]
|
||||
ethstore import-wallet <path> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore find-wallet-pass <path> <password>
|
||||
ethstore remove <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore sign <address> <password> <message> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore public <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
@ -69,6 +76,7 @@ Commands:
|
||||
list List accounts.
|
||||
import Import accounts from src.
|
||||
import-wallet Import presale wallet.
|
||||
find-wallet-pass Tries to open a wallet with list of passwords given.
|
||||
remove Remove account.
|
||||
sign Sign message.
|
||||
public Displays public key for an address.
|
||||
@ -86,6 +94,7 @@ struct Args {
|
||||
cmd_list: bool,
|
||||
cmd_import: bool,
|
||||
cmd_import_wallet: bool,
|
||||
cmd_find_wallet_pass: bool,
|
||||
cmd_remove: bool,
|
||||
cmd_sign: bool,
|
||||
cmd_public: bool,
|
||||
@ -239,6 +248,11 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
|
||||
let vault_ref = open_args_vault(&store, &args)?;
|
||||
let account_ref = store.insert_account(vault_ref, kp.secret().clone(), &password)?;
|
||||
Ok(format!("0x{:?}", account_ref.address))
|
||||
} else if args.cmd_find_wallet_pass {
|
||||
let passwords = load_password(&args.arg_password)?;
|
||||
let passwords = passwords.lines().map(str::to_owned).collect::<VecDeque<_>>();
|
||||
crack::run(passwords, &args.arg_path)?;
|
||||
Ok(format!("Password not found."))
|
||||
} else if args.cmd_remove {
|
||||
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
|
Loading…
Reference in New Issue
Block a user