Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add no_password option for the seed command #57

Merged
merged 2 commits into from
Aug 13, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 55 additions & 29 deletions src/hot/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fs;
use std::env::VarError;
use std::path::{Path, PathBuf};
use std::{env, fs};

use amplify::hex::ToHex;
use amplify::{Display, IoError};
Expand All @@ -35,6 +36,8 @@ use psbt::Psbt;
use crate::hot::{calculate_entropy, DataError, SecureIo, Seed, SeedType};
use crate::Bip43;

const SEED_PASSWORD_ENVVAR: &str = "SEED_PASSWORD";

/// Command-line arguments
#[derive(Parser)]
#[derive(Clone, Eq, PartialEq, Debug)]
Expand All @@ -53,15 +56,19 @@ pub struct HotArgs {

#[derive(Subcommand, Clone, PartialEq, Eq, Debug, Display)]
pub enum HotCommand {
/// Generate new seed and saves it as an encoded file
/// Generate new seed and saves it as an encoded file. The password can be provided via the
/// `SEED_PASSWORD` environment variable (security warning: don't set it on the command line,
/// use instead the shell's builtin `read` and then export it).
#[display("seed")]
Seed {
/// File to save generated seed data and extended master key
output_file: PathBuf,
},

/// Derive new extended private key from the seed and saves it into a separate file as a new
/// signing account
/// signing account. The seed password can be provided via the `SEED_PASSWORD` environment
/// variable (security warning: don't set it on the command line, use instead the shell's
/// builtin `read` and then export it).
#[display("derive")]
Derive {
/// Do not ask for a password and default to an empty-line password. For testing purposes
Expand Down Expand Up @@ -151,24 +158,55 @@ 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_envvar: Option<&str>,
prompt: &str,
accept_weak: bool,
) -> Result<String, std::io::Error> {
let password = loop {
let password = if let Some(varname) = password_envvar {
match env::var(varname) {
Ok(password) => return Ok(password),
Err(VarError::NotUnicode(_)) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"password set by environment is not a valid unicode string",
))
}
Err(VarError::NotPresent) => None,
}
} else {
None
};
let password =
if let Some(pass) = password { pass } else { rpassword::prompt_password(prompt)? };

let entropy = calculate_entropy(&password);
eprintln!("Password entropy: ~{entropy:.0} bits");
if seed_password.is_empty() || entropy < 64.0 {
if !accept_weak && (password.is_empty() || entropy < 64.0) {
eprintln!("Entropy is too low, please try with a different password");
continue;
if password_envvar.is_some() {
return Err(std::io::Error::new(std::io::ErrorKind::Other, "low password entropy"));
} else {
continue;
}
}

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

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

seed.write(output_file, &seed_password)?;
if let Err(e) = Seed::read(output_file, &seed_password) {
Expand Down Expand Up @@ -248,24 +286,12 @@ fn derive(
output_file: &Path,
no_password: bool,
) -> Result<(), DataError> {
let seed_password = rpassword::prompt_password("Seed password: ")?;
let seed_password = get_password(Some(SEED_PASSWORD_ENVVAR), "Seed password:", false)?;

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)?
};

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