Improve recovery

This commit is contained in:
Aditya Kulkarni 2020-05-07 21:41:34 -07:00
parent f50b86ed1e
commit 664e9512bb
4 changed files with 51 additions and 25 deletions

View File

@ -27,6 +27,10 @@ macro_rules! configure_clapapp {
.long("recover")
.help("Attempt to recover the seed from the wallet")
.takes_value(false))
.arg(Arg::with_name("password")
.long("password")
.help("When recovering seed, specify a password for the encrypted wallet")
.takes_value(true))
.arg(Arg::with_name("seed")
.short("s")
.long("seed")
@ -234,7 +238,7 @@ pub fn command_loop(lightclient: Arc<LightClient>) -> (Sender<(String, Vec<Strin
(command_tx, resp_rx)
}
pub fn attempt_recover_seed() {
pub fn attempt_recover_seed(password: Option<String>) {
// Create a Light Client Config in an attempt to recover the file.
let config = LightClientConfig {
server: "0.0.0.0:0".parse().unwrap(),
@ -246,7 +250,7 @@ pub fn attempt_recover_seed() {
data_dir: None,
};
match LightClient::attempt_recover_seed(&config) {
match LightClient::attempt_recover_seed(&config, password) {
Ok(seed) => println!("Recovered seed: '{}'", seed),
Err(e) => eprintln!("Failed to recover seed. Error: {}", e)
};

View File

@ -13,9 +13,10 @@ pub fn main() {
let fresh_app = App::new("Zecwallet CLI");
let configured_app = configure_clapapp!(fresh_app);
let matches = configured_app.get_matches();
if matches.is_present("recover") {
// Create a Light Client Config in an attempt to recover the file.
attempt_recover_seed();
attempt_recover_seed(matches.value_of("password").map(|s| s.to_string()));
return;
}
@ -55,8 +56,9 @@ pub fn main() {
let (command_tx, resp_rx) = match startup(server, dangerous, seed, birthday, !nosync, command.is_none()) {
Ok(c) => c,
Err(e) => {
eprintln!("Error during startup: {}", e);
error!("Error during startup: {}", e);
let emsg = format!("Error during startup:{}\nIf you repeatedly run into this issue, you might have to restore your wallet from your seed phrase.", e);
eprintln!("{}", emsg);
error!("{}", emsg);
if cfg!(target_os = "unix" ) {
match e.raw_os_error() {
Some(13) => report_permission_error(),

View File

@ -418,7 +418,7 @@ impl LightClient {
Ok(())
}
pub fn attempt_recover_seed(config: &LightClientConfig) -> Result<String, String> {
pub fn attempt_recover_seed(config: &LightClientConfig, password: Option<String>) -> Result<String, String> {
use std::io::prelude::*;
use byteorder::{LittleEndian, ReadBytesExt};
use libflate::gzip::Decoder;
@ -442,8 +442,8 @@ impl LightClient {
false
};
if encrypted {
return Err("The wallet is encrypted!".to_string());
if encrypted && password.is_none() {
return Err("The wallet is encrypted and a password was not specified. Please specify the password with '--password'!".to_string());
}
let mut enc_seed = [0u8; 48];
@ -451,19 +451,35 @@ impl LightClient {
reader.read_exact(&mut enc_seed).unwrap();
}
let _nonce = if version >= 4 {
let nonce = if version >= 4 {
Vector::read(&mut reader, |r| r.read_u8()).unwrap()
} else {
vec![]
};
// Seed
let mut seed_bytes = [0u8; 32];
reader.read_exact(&mut seed_bytes).unwrap();
let phrase = if encrypted {
use sodiumoxide::crypto::secretbox;
use crate::lightwallet::double_sha256;
let phrase = Mnemonic::from_entropy(&seed_bytes, Language::English,).unwrap().phrase().to_string();
// Get the doublesha256 of the password, which is the right length
let key = secretbox::Key::from_slice(&double_sha256(password.unwrap().as_bytes())).unwrap();
let nonce = secretbox::Nonce::from_slice(&nonce).unwrap();
Ok(phrase)
let seed = match secretbox::open(&enc_seed, &nonce, &key) {
Ok(s) => s,
Err(_) => return Err("Decryption failed. Is your password correct?".to_string())
};
Mnemonic::from_entropy(&seed, Language::English)
} else {
// Seed
let mut seed_bytes = [0u8; 32];
reader.read_exact(&mut seed_bytes).unwrap();
Mnemonic::from_entropy(&seed_bytes, Language::English)
}.map_err(|e| format!("Failed to read seed. {:?}", e));
phrase.map(|m| m.phrase().to_string())
}
@ -1268,13 +1284,14 @@ pub mod tests {
let seed = lc.do_seed_phrase().unwrap()["seed"].as_str().unwrap().to_string();
lc.do_save().unwrap();
assert_eq!(seed, LightClient::attempt_recover_seed(&config).unwrap());
assert_eq!(seed, LightClient::attempt_recover_seed(&config, None).unwrap());
// Now encrypt and save the file
lc.wallet.write().unwrap().encrypt("password".to_string()).unwrap();
let pwd = "password".to_string();
lc.wallet.write().unwrap().encrypt(pwd.clone()).unwrap();
lc.do_save().unwrap();
assert!(LightClient::attempt_recover_seed(&config).is_err());
assert_eq!(seed, LightClient::attempt_recover_seed(&config, Some(pwd)).unwrap());
}
}

View File

@ -239,12 +239,15 @@ impl LightWallet {
return Err(io::Error::new(ErrorKind::InvalidData, e));
}
println!("Reading wallet version {}", version);
info!("Reading wallet version {}", version);
// After version 5, we're writing the rest of the file as a compressed stream (gzip)
let mut reader: Box<dyn Read> = if version <= 4 {
info!("Reading direct");
Box::new(inp)
} else {
info!("Reading libflat");
Box::new(Decoder::new(inp).unwrap())
};
@ -253,25 +256,25 @@ impl LightWallet {
} else {
false
};
info!("Wallet Encryption {:?}", encrypted);
let mut enc_seed = [0u8; 48];
if version >= 4 {
reader.read_exact(&mut enc_seed)?;
}
let nonce = if version >= 4 {
Vector::read(&mut reader, |r| r.read_u8())?
} else {
vec![]
};
// Seed
let mut seed_bytes = [0u8; 32];
reader.read_exact(&mut seed_bytes)?;
// Read the spending keys
let extsks = Vector::read(&mut reader, |r| ExtendedSpendingKey::read(r))?;
println!("reading version {}", version);
let extfvks = if version >= 4 {
// Read the viewing keys
@ -281,7 +284,7 @@ impl LightWallet {
extsks.iter().map(|sk| ExtendedFullViewingKey::from(sk))
.collect::<Vec<ExtendedFullViewingKey>>()
};
// Calculate the addresses
let addresses = extfvks.iter().map( |fvk| fvk.default_address().unwrap().1 )
.collect::<Vec<PaymentAddress<Bls12>>>();
@ -291,7 +294,7 @@ impl LightWallet {
r.read_exact(&mut tpk_bytes)?;
secp256k1::SecretKey::from_slice(&tpk_bytes).map_err(|e| io::Error::new(ErrorKind::InvalidData, e))
})?;
let taddresses = if version >= 4 {
// Read the addresses
Vector::read(&mut reader, |r| utils::read_string(r))?
@ -299,9 +302,9 @@ impl LightWallet {
// Calculate the addresses
tkeys.iter().map(|sk| LightWallet::address_from_prefix_sk(&config.base58_pubkey_address(), sk)).collect()
};
let blocks = Vector::read(&mut reader, |r| BlockData::read(r))?;
let txs_tuples = Vector::read(&mut reader, |r| {
let mut txid_bytes = [0u8; 32];
r.read_exact(&mut txid_bytes)?;
@ -363,7 +366,7 @@ impl LightWallet {
writer.write_all(&self.seed)?;
// Flush after writing the seed, so in case of a disaster, we can still recover the seed.
//writer.flush()?;
writer.flush()?;
// Write all the spending keys
Vector::write(&mut writer, &self.extsks.read().unwrap(),