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).
This commit is contained in:
+7
-7
@@ -39,6 +39,7 @@ struct Cli {
|
|||||||
raw_key: Option<Zeroizing<String>>,
|
raw_key: Option<Zeroizing<String>>,
|
||||||
|
|
||||||
/// Read passphrase interactively (terminal). Implies argon2id KDF on encrypt.
|
/// Read passphrase interactively (terminal). Implies argon2id KDF on encrypt.
|
||||||
|
/// This is the default when no key source is specified.
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
passphrase: bool,
|
passphrase: bool,
|
||||||
|
|
||||||
@@ -131,8 +132,13 @@ fn run(mut cli: Cli) -> Result<(), FcryError> {
|
|||||||
let raw_key_str: Option<Zeroizing<String>> = cli.raw_key.take();
|
let raw_key_str: Option<Zeroizing<String>> = cli.raw_key.take();
|
||||||
let pw_src: Option<PassphraseSource> = if cli.passphrase {
|
let pw_src: Option<PassphraseSource> = if cli.passphrase {
|
||||||
Some(PassphraseSource::Tty)
|
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 {
|
} else {
|
||||||
cli.passphrase_env.take().map(PassphraseSource::EnvVar)
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let decrypt_mode = cli.decrypt;
|
let decrypt_mode = cli.decrypt;
|
||||||
@@ -144,12 +150,6 @@ fn run(mut cli: Cli) -> Result<(), FcryError> {
|
|||||||
let argon_parallelism = cli.argon_parallelism;
|
let argon_parallelism = cli.argon_parallelism;
|
||||||
drop(cli);
|
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 {
|
if decrypt_mode {
|
||||||
let raw_key = match raw_key_str.as_deref() {
|
let raw_key = match raw_key_str.as_deref() {
|
||||||
Some(s) => Some(parse_raw_key(s)?),
|
Some(s) => Some(parse_raw_key(s)?),
|
||||||
|
|||||||
Reference in New Issue
Block a user