use anyhow::Result;
use clap::{Parser, Subcommand, ValueEnum};
#[derive(Debug, Parser)]
#[command(
name = "hsh",
version,
about = "Enterprise password hashing for the command line.",
long_about = None,
)]
pub(crate) struct Cli {
#[arg(long, global = true)]
pub json: bool,
#[command(subcommand)]
pub command: Command,
}
#[derive(Debug, Subcommand)]
pub(crate) enum Command {
Hash(HashArgs),
Verify(VerifyArgs),
Rehash(RehashArgs),
Inspect(InspectArgs),
#[command(
long_about = "Show the effective crypto route for a given preset: \
which Backend the policy declares, whether this build \
can satisfy a FIPS requirement, the primary algorithm \
new hashes are minted under, whether the `pepper` \
feature is compiled in, and build provenance (hsh-cli \
version, rustc, target triple, profile). Use this \
before a deployment takes traffic to verify the binary \
actually delivers the contract you expected."
)]
InspectBackend(InspectBackendArgs),
Calibrate(CalibrateArgs),
Completions(CompletionsArgs),
}
#[derive(Debug, Clone, Copy, ValueEnum, Default)]
pub(crate) enum PresetPolicy {
#[default]
Owasp,
Rfc9106,
Fips,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
pub(crate) enum AlgoArg {
Argon2id,
Argon2i,
Argon2d,
Bcrypt,
Scrypt,
Pbkdf2,
}
#[derive(Debug, Clone, clap::Args)]
pub(crate) struct HashArgs {
#[arg(long, value_enum, default_value_t)]
pub policy: PresetPolicy,
#[arg(short, long, value_enum)]
pub algorithm: Option<AlgoArg>,
#[arg(long, env = "HSH_PASSWORD", hide_env_values = true)]
pub password: Option<String>,
}
#[derive(Debug, Clone, clap::Args)]
pub(crate) struct VerifyArgs {
#[arg(short = 'H', long, env = "HSH_STORED")]
pub stored: String,
#[arg(long, value_enum, default_value_t)]
pub policy: PresetPolicy,
#[arg(long, env = "HSH_PASSWORD", hide_env_values = true)]
pub password: Option<String>,
}
#[derive(Debug, Clone, clap::Args)]
pub(crate) struct RehashArgs {
#[arg(short = 'H', long, env = "HSH_STORED")]
pub stored: String,
#[arg(long, value_enum, default_value_t)]
pub policy: PresetPolicy,
#[arg(long, env = "HSH_PASSWORD", hide_env_values = true)]
pub password: Option<String>,
}
#[derive(Debug, Clone, clap::Args)]
pub(crate) struct InspectArgs {
pub hash: String,
}
#[derive(Debug, Clone, clap::Args)]
pub(crate) struct InspectBackendArgs {
#[arg(long, value_enum, default_value_t)]
pub policy: PresetPolicy,
}
#[derive(Debug, Clone, clap::Args)]
pub(crate) struct CalibrateArgs {
#[arg(short, long, value_enum, default_value_t = AlgoArg::Argon2id)]
pub algorithm: AlgoArg,
#[arg(short = 't', long, default_value_t = 500)]
pub target_ms: u32,
}
#[derive(Debug, Clone, clap::Args)]
pub(crate) struct CompletionsArgs {
#[arg(value_enum)]
pub shell: clap_complete::Shell,
}
impl Cli {
pub(crate) fn run(self) -> Result<()> {
match self.command {
Command::Hash(args) => {
crate::commands::hash::run(args, self.json)
}
Command::Verify(args) => {
crate::commands::verify::run(args, self.json)
}
Command::Rehash(args) => {
crate::commands::rehash::run(args, self.json)
}
Command::Inspect(args) => {
crate::commands::inspect::run(args, self.json)
}
Command::InspectBackend(args) => {
crate::commands::inspect_backend::run(args, self.json)
}
Command::Calibrate(args) => {
crate::commands::calibrate::run(args, self.json)
}
Command::Completions(args) => {
crate::commands::completions::run(args)
}
}
}
}