Skip to content

Commit

Permalink
add seed_password option to seed and derive commands
Browse files Browse the repository at this point in the history
  • Loading branch information
nicbus committed Aug 8, 2024
1 parent 4660bf3 commit 1154efa
Showing 1 changed file with 57 additions and 28 deletions.
85 changes: 57 additions & 28 deletions src/hot/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ pub enum HotCommand {
Seed {
/// File to save generated seed data and extended master key
output_file: PathBuf,

/// Provide the password to be used for the seed
#[clap(short = 'p', long)]
seed_password: Option<String>,
},

/// Derive new extended private key from the seed and saves it into a separate file as a new
Expand All @@ -72,6 +76,10 @@ pub enum HotCommand {
/// Seed file containing extended master key, created previously with `seed` command
seed_file: PathBuf,

/// Provide the password to be used for the seed
#[clap(long)]
seed_password: Option<String>,

/// Derivation scheme.
#[clap(short, long, default_value = "bip86")]
scheme: Bip43,
Expand Down Expand Up @@ -127,15 +135,27 @@ pub enum HotCommand {
impl HotArgs {
pub fn exec(self) -> Result<(), DataError> {
match self.command {
HotCommand::Seed { output_file } => seed(&output_file)?,
HotCommand::Seed {
output_file,
seed_password,
} => seed(&output_file, &seed_password)?,

Check warning on line 141 in src/hot/command.rs

View check run for this annotation

Codecov / codecov/patch

src/hot/command.rs#L139-L141

Added lines #L139 - L141 were not covered by tests
HotCommand::Derive {
no_password,
seed_file,
seed_password,

Check warning on line 145 in src/hot/command.rs

View check run for this annotation

Codecov / codecov/patch

src/hot/command.rs#L145

Added line #L145 was not covered by tests
scheme,
account,
mainnet,
output_file,
} => derive(&seed_file, scheme, account, mainnet, &output_file, no_password)?,
} => derive(
&seed_file,
&seed_password,
scheme,
account,
mainnet,
&output_file,
no_password,
)?,

Check warning on line 158 in src/hot/command.rs

View check run for this annotation

Codecov / codecov/patch

src/hot/command.rs#L150-L158

Added lines #L150 - L158 were not covered by tests
HotCommand::Info {
file,
print_private,
Expand All @@ -151,24 +171,44 @@ impl HotArgs {
}
}

fn seed(output_file: &Path) -> Result<(), DataError> {
let seed = Seed::random(SeedType::Bit128);
let seed_password = loop {
let seed_password = rpassword::prompt_password("Seed password: ")?;
let entropy = calculate_entropy(&seed_password);
fn get_password(
password_arg: &Option<String>,
prompt: &str,
accept_weak: bool,
) -> Result<String, std::io::Error> {
let password = loop {
let password = if let Some(p) = password_arg {
p.clone()

Check warning on line 181 in src/hot/command.rs

View check run for this annotation

Codecov / codecov/patch

src/hot/command.rs#L174-L181

Added lines #L174 - L181 were not covered by tests
} else {
rpassword::prompt_password(prompt)?

Check warning on line 183 in src/hot/command.rs

View check run for this annotation

Codecov / codecov/patch

src/hot/command.rs#L183

Added line #L183 was not covered by tests
};

let entropy = calculate_entropy(&password);

Check warning on line 186 in src/hot/command.rs

View check run for this annotation

Codecov / codecov/patch

src/hot/command.rs#L186

Added line #L186 was not covered by tests
eprintln!("Password entropy: ~{entropy:.0} bits");
if seed_password.is_empty() || entropy < 64.0 {
if !accept_weak && (password.is_empty() || entropy < 64.0) {

Check warning on line 188 in src/hot/command.rs

View check run for this annotation

Codecov / codecov/patch

src/hot/command.rs#L188

Added line #L188 was not covered by tests
eprintln!("Entropy is too low, please try with a different password");
continue;
if password_arg.is_some() {
return Err(std::io::Error::new(std::io::ErrorKind::Other, "low entropy"));

Check warning on line 191 in src/hot/command.rs

View check run for this annotation

Codecov / codecov/patch

src/hot/command.rs#L190-L191

Added lines #L190 - L191 were not covered by tests
} else {
continue;

Check warning on line 193 in src/hot/command.rs

View check run for this annotation

Codecov / codecov/patch

src/hot/command.rs#L193

Added line #L193 was not covered by tests
}
}

let password2 = rpassword::prompt_password("Repeat the password: ")?;
if password2 != seed_password {
eprintln!("Passwords do not match, please try again");
continue;
if password_arg.is_none() {
let repeat = rpassword::prompt_password("Repeat the password: ")?;
if repeat != password {
eprintln!("Passwords do not match, please try again");
continue;
}

Check warning on line 202 in src/hot/command.rs

View check run for this annotation

Codecov / codecov/patch

src/hot/command.rs#L197-L202

Added lines #L197 - L202 were not covered by tests
}
break seed_password;
break password;

Check warning on line 204 in src/hot/command.rs

View check run for this annotation

Codecov / codecov/patch

src/hot/command.rs#L204

Added line #L204 was not covered by tests
};
Ok(password)
}

Check warning on line 207 in src/hot/command.rs

View check run for this annotation

Codecov / codecov/patch

src/hot/command.rs#L206-L207

Added lines #L206 - L207 were not covered by tests

fn seed(output_file: &Path, seed_password: &Option<String>) -> Result<(), DataError> {
let seed = Seed::random(SeedType::Bit128);
let seed_password = get_password(seed_password, "Seed password:", false)?;

Check warning on line 211 in src/hot/command.rs

View check run for this annotation

Codecov / codecov/patch

src/hot/command.rs#L209-L211

Added lines #L209 - L211 were not covered by tests

seed.write(output_file, &seed_password)?;
if let Err(e) = Seed::read(output_file, &seed_password) {
Expand Down Expand Up @@ -242,30 +282,19 @@ fn info_account(account: XprivAccount, print_private: bool) {

fn derive(
seed_file: &Path,
seed_password: &Option<String>,

Check warning on line 285 in src/hot/command.rs

View check run for this annotation

Codecov / codecov/patch

src/hot/command.rs#L285

Added line #L285 was not covered by tests
scheme: Bip43,
account: HardenedIndex,
mainnet: bool,
output_file: &Path,
no_password: bool,
) -> Result<(), DataError> {
let seed_password = rpassword::prompt_password("Seed password: ")?;
let seed_password = get_password(seed_password, "Seed password:", false)?;

Check warning on line 292 in src/hot/command.rs

View check run for this annotation

Codecov / codecov/patch

src/hot/command.rs#L292

Added line #L292 was not covered by tests

let account_password = if !mainnet && no_password {
s!("")
} else {
loop {
let account_password = rpassword::prompt_password("Account password: ")?;
let entropy = calculate_entropy(&seed_password);
eprintln!("Password entropy: ~{entropy:.0} bits");
if !account_password.is_empty() && entropy >= 64.0 {
break account_password;
}
if !mainnet {
eprintln!("Entropy is too low, but since we are on testnet we accept that");
break account_password;
}
eprintln!("Entropy is too low, please try with a different password")
}
get_password(&None, "Account password:", !mainnet)?

Check warning on line 297 in src/hot/command.rs

View check run for this annotation

Codecov / codecov/patch

src/hot/command.rs#L297

Added line #L297 was not covered by tests
};

let seed = Seed::read(seed_file, &seed_password)?;
Expand Down

0 comments on commit 1154efa

Please sign in to comment.