Wallet recovery phrase security fixes (#2276)

* prompt for recovery phrase using linefeed crate functionality
* break out recovery parse function, remove phrase from logging
This commit is contained in:
Yeastplume 2019-01-02 20:10:07 +00:00 committed by Ignotus Peverell
parent f88ffe5fd6
commit d9ff8e278e
7 changed files with 161 additions and 38 deletions

93
Cargo.lock generated
View file

@ -124,6 +124,11 @@ dependencies = [
"which 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "0.9.1"
@ -681,6 +686,7 @@ dependencies = [
"grin_util 0.5.0",
"grin_wallet 0.5.0",
"humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"linefeed 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"reqwest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rpassword 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1210,6 +1216,16 @@ dependencies = [
"vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "linefeed"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"mortal 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "linked-hash-map"
version = "0.4.2"
@ -1422,6 +1438,21 @@ dependencies = [
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mortal"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallstr 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"terminfo 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "msdos_time"
version = "0.1.6"
@ -1468,6 +1499,19 @@ dependencies = [
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nix"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nix"
version = "0.11.0"
@ -1493,6 +1537,14 @@ dependencies = [
"memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nom"
version = "4.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num"
version = "0.1.42"
@ -2044,6 +2096,14 @@ name = "rustc-serialize"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc_version"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
@ -2136,6 +2196,11 @@ dependencies = [
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "semver"
version = "0.9.0"
@ -2235,6 +2300,14 @@ name = "slab"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "smallstr"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "smallvec"
version = "0.6.7"
@ -2345,6 +2418,17 @@ dependencies = [
"wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "terminfo"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"nom 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)",
"phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "termion"
version = "1.5.1"
@ -2904,6 +2988,7 @@ dependencies = [
"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0"
"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
"checksum bindgen 0.37.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1b25ab82877ea8fe6ce1ce1f8ac54361f0218bad900af9eb11803994bf67c221"
"checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3"
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400"
@ -2995,6 +3080,7 @@ dependencies = [
"checksum liblmdb-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "feed38a3a580f60bf61aaa067b0ff4123395966839adeaf67258a9e50c4d2e49"
"checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2"
"checksum libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe"
"checksum linefeed 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2abb5810ef55bb5f5f33b010cc280b3ab877764c902681efc7c8c95628004c"
"checksum linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7860ec297f7008ff7a1e3382d7f7e1dcd69efc94751a2284bafc3d013c2aa939"
"checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e"
"checksum lmdb-zero 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "13416eee745b087c22934f35f1f24da22da41ba2a5ce197143d168ce055cc58d"
@ -3018,13 +3104,16 @@ dependencies = [
"checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432"
"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
"checksum mortal 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "26153280e6a955881f761354b130aa7838f9983836f3de438ac0a8f22cfab1ff"
"checksum msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aad9dfe950c057b1bfe9c1f2aa51583a8468ef2a5baba2ebbe06d775efeb7729"
"checksum native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8e08de0070bbf4c31f452ea2a70db092f36f6f2e4d897adf5674477d488fb2"
"checksum ncurses 5.97.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee821144e7fe6fd1d1c04b8001d92d783ae471a71d60ab506e6c608b83a85ae6"
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
"checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17"
"checksum nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0d95c5fa8b641c10ad0b8887454ebaafa3c92b5cd5350f8fc693adafd178e7b"
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
"checksum nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05aec50c70fd288702bcd93284a8444607f3292dbdf2a30de5ea5dcdbe72287b"
"checksum nom 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9c349f68f25f596b9f44cf0e7c69752a5c633b0550c3ff849518bfba0233774a"
"checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e"
"checksum num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf4825417e1e1406b3782a8ce92f4d53f26ec055e3622e1881ca8e9f5f9e08db"
"checksum num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1"
@ -3086,6 +3175,7 @@ dependencies = [
"checksum rpassword 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37473170aedbe66ffa3ad3726939ba677d83c646ad4fd99e5b4bc38712f45ec"
"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum rustls 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "942b71057b31981152970d57399c25f72e27a6ee0d207a669d8304cabf44705b"
"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7"
@ -3098,6 +3188,7 @@ dependencies = [
"checksum sct 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb8f61f9e6eadd062a71c380043d28036304a4706b3c4dd001ff3387ed00745a"
"checksum security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "697d3f3c23a618272ead9e1fb259c1411102b31c6af8b93f1d64cca9c3b0e8e0"
"checksum security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab01dfbe5756785b5b4d46e0289e5a18071dfa9a7c2b24213ea00b9ef9b665bf"
"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.81 (registry+https://github.com/rust-lang/crates.io-index)" = "c91eb5b0190ae87b4e2e39cbba6e3bed3ac6186935fe265f0426156c4c49961b"
@ -3110,6 +3201,7 @@ dependencies = [
"checksum signal-hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8941ae94fa73d0f73b422774b3a40a7195cecd88d1c090f4b37ade7dc795ab66"
"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
"checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d"
"checksum smallstr 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aa65bb4d5b2bbc90d36af64e29802f788aa614783fa1d0df011800ddcec6e8e"
"checksum smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b73ea3738b47563803ef814925e69be00799a8c07420be8b996f8e98fb2336db"
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
"checksum string 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98998cced76115b1da46f63388b909d118a37ae0be0f82ad35773d4a4bc9d18d"
@ -3123,6 +3215,7 @@ dependencies = [
"checksum term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6b677dd1e8214ea1ef4297f85dbcbed8e8cdddb561040cc998ca2551c37561"
"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327"
"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f"
"checksum terminfo 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8e51065bafd2abe106b6036483b69d1741f4a1ec56ce8a2378de341637de689e"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
"checksum thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1"

View file

@ -32,6 +32,7 @@ serde = "1"
serde_json = "1"
log = "0.4"
term = "0.5"
linefeed = "0.5"
failure = "0.1"
failure_derive = "0.1"

View file

@ -110,21 +110,19 @@ the correct recovery phrase,) your wallet contents should again be usable.
To recover your wallet seed, delete (or backup) the wallet's `wallet_data/wallet.seed` file, then run:
```sh
grin wallet recover -p "[12 or 24 word passphrase separated by spaces"
```
e.g:
```sh
grin wallet recover -p "shiver alarm excuse turtle absorb surface lunch virtual want remind hard slow vacuum park silver asthma engage library battle jelly buffalo female inquiry wire"
grin wallet recover
```
You will be prompted for your recovery phrase as well as a new password for your seed file.
If you're restoring a wallet from scratch, you'll then need to use the `grin wallet restore` command to scan the chain
for your outputs and restore them. See the `grin wallet restore` command below for details of the entire process.
You can also view your recovery phrase with your password by running the recover command without any arguments, e.g:
You can also view your recovery phrase with your password by running the recover command with the -d (display) argument, e.g:
```sh
grin wallet recover
grin wallet recover -d
Password:
Your recovery phrase is:
shiver alarm excuse turtle absorb surface lunch virtual want remind hard slow vacuum park silver asthma engage library battle jelly buffalo female inquiry wire

View file

@ -22,6 +22,8 @@ use grin_core as core;
use grin_keychain as keychain;
use grin_wallet::{command, instantiate_wallet, NodeClient, WalletConfig, WalletInst, WalletSeed};
use grin_wallet::{Error, ErrorKind};
use linefeed::terminal::Signal;
use linefeed::{Interface, ReadResult};
use rpassword;
use std::path::Path;
use std::sync::Arc;
@ -43,6 +45,16 @@ macro_rules! arg_parse {
pub enum ParseError {
#[fail(display = "Invalid Arguments: {}", _0)]
ArgumentError(String),
#[fail(display = "Parsing IO error: {}", _0)]
IOError(String),
#[fail(display = "User Cancelled")]
CancelledError,
}
impl From<std::io::Error> for ParseError {
fn from(e: std::io::Error) -> ParseError {
ParseError::IOError(format!("{}", e))
}
}
pub fn prompt_password(password: &Option<String>) -> String {
@ -53,15 +65,47 @@ pub fn prompt_password(password: &Option<String>) -> String {
}
fn prompt_password_confirm() -> String {
let first = rpassword::prompt_password_stdout("Password: ").unwrap();
let second = rpassword::prompt_password_stdout("Confirm Password: ").unwrap();
if first != second {
println!("Passwords do not match");
std::process::exit(0);
let mut first = String::from("first");
let mut second = String::from("second");
while first != second {
first = rpassword::prompt_password_stdout("Password: ").unwrap();
second = rpassword::prompt_password_stdout("Confirm Password: ").unwrap();
}
first
}
fn prompt_recovery_phrase() -> Result<String, ParseError> {
let interface = Arc::new(Interface::new("recover")?);
let mut phrase = String::new();
interface.set_report_signal(Signal::Interrupt, true);
interface.set_prompt("phrase> ")?;
loop {
println!("Please enter your recovery phrase:");
let res = interface.read_line()?;
match res {
ReadResult::Eof => break,
ReadResult::Signal(sig) => {
if sig == Signal::Interrupt {
interface.cancel_read_line()?;
return Err(ParseError::CancelledError);
}
}
ReadResult::Input(line) => {
if WalletSeed::from_mnemonic(&line).is_ok() {
phrase = line;
break;
} else {
println!();
println!("Recovery word phrase is invalid.");
println!();
interface.set_buffer(&line)?;
}
}
}
}
Ok(phrase)
}
// instantiate wallet (needed by most functions)
pub fn inst_wallet(
@ -179,15 +223,12 @@ pub fn parse_recover_args(
args: &ArgMatches,
) -> Result<command::RecoverArgs, ParseError> {
let (passphrase, recovery_phrase) = {
match args.value_of("recovery_phrase") {
None => (prompt_password(&g_args.password), None),
Some(l) => {
if WalletSeed::from_mnemonic(l).is_err() {
let msg = format!("Recovery word phrase is invalid");
return Err(ParseError::ArgumentError(msg));
}
match args.is_present("display") {
true => (prompt_password(&g_args.password), None),
false => {
let phrase = prompt_recovery_phrase()?;
println!("Please provide a new password for the recovered wallet");
(prompt_password_confirm(), Some(l.to_owned()))
(prompt_password_confirm(), Some(phrase.to_owned()))
}
}
};

View file

@ -285,13 +285,13 @@ subcommands:
long: short_wordlist
takes_value: false
- recover:
about: recover (create a new wallet.seed file) from a recovery phrase
about: Recover a wallet.seed file from a recovery phrase (default) or displays a recovery phrase for an existing seed file
args:
- recovery_phrase:
help: 12 or 24 word recovery phrase (encased in quotes).
short: p
long: recovery_phrase
takes_value: true
- display:
help: Display wallet recovery phrase
short: d
long: display
takes_value: false
- restore:
about: Restores a wallet contents from a seed file
- check_repair:

View file

@ -87,11 +87,7 @@ pub fn recover(config: &WalletConfig, args: RecoverArgs) -> Result<(), Error> {
&args.passphrase,
);
if let Err(e) = res {
error!(
"Error recovering seed with list '{}' - {}",
&args.recovery_phrase.as_ref().unwrap(),
e
);
error!("Error recovering seed - {}", e);
return Err(e);
}
}

View file

@ -49,13 +49,7 @@ where
// just read the wallet here, no need for a write lock
let mut outputs = wallet
.iter()
.filter(|out| {
if show_spent {
true
} else {
out.status != OutputStatus::Spent
}
})
.filter(|out| show_spent || out.status != OutputStatus::Spent)
.collect::<Vec<_>>();
// only include outputs with a given tx_id if provided