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
+54 -13
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()))
}
}
};
+6 -6
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: