From f72f9034f3a6f17c3b56a069518c0b1175a03169 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sat, 2 May 2026 19:52:19 +0200 Subject: [PATCH] feat(cli): default to interactive passphrase when no key source given Previously, invoking fcry without any of --raw-key, --passphrase, or --passphrase-env produced a hard error ("must provide one of ..."). The common, secure case (interactive TTY passphrase) thus required an explicit flag, while the dangerous case (--raw-key on the command line) was equally accessible. Make the secure path the default: if no key source is specified, fall back to PassphraseSource::Tty, which prompts on the terminal and runs argon2id on encrypt. Explicit --passphrase still works and is now redundant for the default invocation; --raw-key and --passphrase-env remain unchanged and still suppress the default. The previous "must provide one of ..." error path becomes unreachable and is removed: the only way pw_src is None is when raw_key_str is Some, which is handled by the existing encrypt/decrypt branches. User-visible change: `fcry -i foo -o foo.enc` now prompts for a passphrase instead of erroring out. Scripts that relied on the error to detect missing arguments will instead block on a TTY read; non-TTY callers should continue to pass --passphrase-env or --raw-key explicitly. Test Plan: - `fcry -i plain -o plain.enc` prompts twice (passphrase + confirm), then `fcry -d -i plain.enc -o plain.out` prompts once and round-trips. - `fcry --raw-key $(head -c32 /dev/urandom | base64) ...` still works and does not prompt. - `PW=hunter2 fcry --passphrase-env PW ...` still works and does not prompt. - `fcry --passphrase --raw-key ...` still rejected by clap (conflicts_with_all). --- src/main.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index 118e92e..03e28d5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,6 +39,7 @@ struct Cli { raw_key: Option>, /// Read passphrase interactively (terminal). Implies argon2id KDF on encrypt. + /// This is the default when no key source is specified. #[clap(short, long)] passphrase: bool, @@ -131,8 +132,13 @@ fn run(mut cli: Cli) -> Result<(), FcryError> { let raw_key_str: Option> = cli.raw_key.take(); let pw_src: Option = if cli.passphrase { Some(PassphraseSource::Tty) + } else if let Some(var) = cli.passphrase_env.take() { + Some(PassphraseSource::EnvVar(var)) + } else if raw_key_str.is_none() { + // Default to interactive TTY passphrase when no key source is given. + Some(PassphraseSource::Tty) } else { - cli.passphrase_env.take().map(PassphraseSource::EnvVar) + None }; let decrypt_mode = cli.decrypt; @@ -144,12 +150,6 @@ fn run(mut cli: Cli) -> Result<(), FcryError> { let argon_parallelism = cli.argon_parallelism; drop(cli); - if pw_src.is_none() && raw_key_str.is_none() { - return Err(FcryError::Format( - "must provide one of --raw-key, --passphrase, --passphrase-env".into(), - )); - } - if decrypt_mode { let raw_key = match raw_key_str.as_deref() { Some(s) => Some(parse_raw_key(s)?),