use super::*;
#[derive(Debug, clap::Subcommand)]
pub enum CommandSecret {
Generate,
Zero,
Id {
#[arg(short, long, value_name = "FORMAT", default_value = "base58")]
format: BinFormat,
},
Passphrase { passphrase: Option<String> },
Export {
#[arg(short, long, value_name = "FORMAT", default_value = "hex")]
format: BinFormat,
},
Save {
#[arg(value_name = "FILENAME")]
filepath: std::path::PathBuf,
#[arg(short = 'H', long)]
hex: bool,
},
#[cfg(feature = "share")]
Share {
#[arg(value_name = "k")]
k: u8,
#[arg(value_name = "n")]
n: u8,
#[arg(short, long, default_value = "base58")]
format: BinFormat,
},
#[cfg(feature = "share")]
Recover,
Load {
#[arg(value_name = "FILENAME")]
filepath: std::path::PathBuf,
},
}
impl CommandSecret {
pub fn process<T: AsMut<S>, S: ToolState, W: Write>(
&self,
mut tool_state: T,
out: &mut W,
) -> Result<(), Error> {
let tool_state = tool_state.as_mut();
match self {
CommandSecret::Generate => {
tool_state.generate()?;
write!(out, "Created {}", tool_state.current_secret()?.id())?;
Ok(())
}
CommandSecret::Zero => {
tool_state.import(&Secret::ZERO)?;
write!(out, "Imported {}", tool_state.current_secret()?.id())?;
Ok(())
}
CommandSecret::Id { format } => {
format.write(out, &tool_state.current_secret()?.id().into_bytes())?;
Ok(())
}
CommandSecret::Export { format } => {
format.write(out, &tool_state.current_secret()?.bytes()?)?;
Ok(())
}
CommandSecret::Save { filepath, hex } => {
if *hex {
bail!("Hex not supported");
}
tool_state.save(filepath)?;
Ok(())
}
CommandSecret::Load { filepath } => {
tool_state.load(filepath)?;
write!(out, "Loaded {}", tool_state.current_secret()?.id())?;
Ok(())
}
CommandSecret::Passphrase { passphrase } => {
if let Some(passphrase) = passphrase.as_ref() {
tool_state.import(&Secret::from_passphrase(passphrase))?;
} else {
let passphrase = rpassword::prompt_password("Enter passphrase: ")?;
if passphrase.is_empty() {
return Ok(());
}
let passphrase_check = rpassword::prompt_password("Verify passphrase: ")?;
if passphrase != passphrase_check {
bail!("Passphrases do not match");
}
tool_state.import(&Secret::from_passphrase(passphrase))?;
}
write!(out, "Imported {}", tool_state.current_secret()?.id())?;
Ok(())
}
#[cfg(feature = "share")]
CommandSecret::Share { k: m, n, format } => {
let shares = tool_state.export()?.split_shares(*n, *m)?;
for share in shares {
format.write(out, &share)?;
writeln!(out)?;
}
Ok(())
}
#[cfg(feature = "share")]
CommandSecret::Recover => {
let mut shares = vec![];
loop {
let prompt_str = if let Ok(secret) = Secret::try_from_shares(&shares) {
format!("Press return to use `{}`, or enter share: ", secret.id())
} else {
"Enter Share: ".to_string()
};
let share_str = rpassword::prompt_password(&prompt_str)?;
if share_str.is_empty() {
break;
} else {
let share = if let Ok(share) =
BinFormat::try_from_str_len(share_str, Secret::SHARE_LEN)
{
share
} else {
eprintln!("Invalid share");
continue;
};
if let Err(err) = Secret::verify_share(&share) {
eprintln!("Invalid share ({})", err);
continue;
}
shares.push(share);
}
}
tool_state.import(&Secret::try_from_shares(&shares)?)?;
write!(out, "Imported {}", tool_state.current_secret()?.id())?;
Ok(())
}
}
}
}