Don't terminate the program if the user doesn't provide a password.

- When encrypting a message with a password, don't terminate the
    program if user doesn't provide a password.  Instead, show an
    error, and prompt the user for a password again.
This commit is contained in:
Neal H. Walfield 2024-08-16 10:00:09 +02:00
parent 9e38b2d9e2
commit 9836873d7a
No known key found for this signature in database
GPG Key ID: 6863C9AD5B4D22D3
5 changed files with 58 additions and 16 deletions

View File

@ -108,18 +108,15 @@ pub fn encrypt<'a, 'b: 'a>(
let mut passwords: Vec<crypto::Password> = Vec::with_capacity(npasswords);
for n in 0..npasswords {
let nprompt;
if let Some(password) = password::prompt_for_new(
let password = password::prompt_for_new(
if npasswords > 1 {
nprompt = format!("message (password {})", n + 1);
&nprompt
} else {
"message"
},
)? {
passwords.push(password)
} else {
return Err(anyhow::anyhow!("Password can not be empty!"));
}
)?;
passwords.push(password);
}
if recipients.len() + passwords.len() == 0 {

View File

@ -123,7 +123,7 @@ pub fn generate(
if ! command.without_password {
builder = builder.set_password(
password::prompt_for_new("key")?);
password::prompt_for_new_or_none("key")?);
}
let on_keystore = command.output.is_none();

View File

@ -273,7 +273,7 @@ fn subkey_add(
if command.without_password {
(key, None)
} else {
(key, common::password::prompt_for_new("subkey")?)
(key, common::password::prompt_for_new_or_none("subkey")?)
}
}
Err(error) => {

View File

@ -37,7 +37,7 @@ pub fn password(sq: Sq,
} else if let Some(path) = new_password_file.as_ref() {
Some(Some(std::fs::read(path)?.into()))
} else {
Some(common::password::prompt_for_new("key")?)
Some(common::password::prompt_for_new_or_none("key")?)
};
}

View File

@ -8,7 +8,39 @@ use sequoia_openpgp as openpgp;
/// Prompt to repeat a password.
const REPEAT_PROMPT: &str = "Please repeat the password";
/// Prompts twice for a new password and returns an optional [`Password`].
/// Prompts the user to enter a new password.
///
/// This function is intended for creating artifacts. For example, if
/// a new key or subkey is generated, or a message should be encrypted
/// using a password. The cost of mistyping is high, so we prompt
/// twice.
///
/// If the two entered passwords match, the result is returned.
///
/// If the passwords differ or no password was entered, an error
/// message is printed and the process is repeated.
pub fn prompt_for_new(reason: &str) -> Result<Password> {
prompt_for_new_internal(reason, false)
.map(|p| p.expect("is not None"))
}
/// Prompts the user to enter an optional new password.
///
/// This function is intended for creating artifacts. For example, if
/// a new key or subkey is generated, or a message should be encrypted
/// using a password. The cost of mistyping is high, so we prompt
/// twice.
///
/// If the two entered passwords match, the result is returned. If no
/// password was entered, `Ok(None)` is returned.
///
/// If the passwords differ, an error message is printed and the
/// process is repeated.
pub fn prompt_for_new_or_none(reason: &str) -> Result<Option<Password>> {
prompt_for_new_internal(reason, true)
}
/// Prompts the user to enter a new password.
///
/// This function is intended for creating artifacts. For example, if
/// a new key or subkey is generated, or a message should be encrypted
@ -16,21 +48,34 @@ const REPEAT_PROMPT: &str = "Please repeat the password";
/// twice.
///
/// If the two entered passwords match, the result is returned. If
/// the password was the empty string, `None` is returned.
/// `allow_none` is `true`, and no password was entered, `Ok(None)` is
/// returned.
///
/// If the passwords differ, an error message is printed and the
/// process is repeated.
pub fn prompt_for_new(
/// If the passwords differ or `allow_none` is `false` and no password
/// was entered, an error message is printed and the process is
/// repeated.
fn prompt_for_new_internal(
reason: &str,
allow_none: bool,
) -> Result<Option<Password>> {
let prompt = format!("Please enter the password to protect {} \
(press enter to not use a password)", reason);
let mut prompt = format!("Please enter the password to protect {}", reason);
if allow_none {
prompt.push_str(" (press enter to not use a password)");
}
let width = prompt.len().max(REPEAT_PROMPT.len());
let p0 = format!("{:>1$}: ", prompt, width);
let p1 = format!("{:>1$}: ", REPEAT_PROMPT, width);
loop {
let password = prompt_password(&p0)?;
if password.is_empty() && allow_none {
wprintln!("Password required. Please try again.");
wprintln!();
continue;
}
let password_repeat = prompt_password(&p1)?;
if password != password_repeat {