refactor passkey/password prompts so they are always SecretString (#281)

This commit is contained in:
Carson McManus 2023-07-05 08:35:32 -04:00 committed by GitHub
parent d9261d0ac8
commit df47ff1823
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 42 additions and 30 deletions

View file

@ -45,8 +45,7 @@ fn load_accounts_with_prompts(manager: &mut AccountManager) -> anyhow::Result<()
if manager.has_passkey() { if manager.has_passkey() {
error!("Incorrect passkey"); error!("Incorrect passkey");
} }
let passkey = rpassword::prompt_password_stdout("Enter encryption passkey: ").ok(); let passkey = Some(crate::tui::prompt_passkey()?);
let passkey = passkey.map(SecretString::new);
manager.submit_passkey(passkey); manager.submit_passkey(passkey);
} }
Err(e) => { Err(e) => {

View file

@ -1,4 +1,5 @@
use log::*; use log::*;
use secrecy::ExposeSecret;
use crate::{ use crate::{
encryption::{EncryptionScheme, EntryEncryptor}, encryption::{EncryptionScheme, EntryEncryptor},
@ -17,23 +18,22 @@ where
{ {
fn execute(&self, _transport: T, manager: &mut AccountManager) -> anyhow::Result<()> { fn execute(&self, _transport: T, manager: &mut AccountManager) -> anyhow::Result<()> {
if !manager.has_passkey() { if !manager.has_passkey() {
let mut passkey; let passkey: Option<SecretString>;
loop { loop {
passkey = rpassword::prompt_password_stdout("Enter encryption passkey: ").ok(); let passkey1 = tui::prompt_passkey()?;
if let Some(p) = passkey.as_ref() { if passkey1.expose_secret().is_empty() {
if p.is_empty() { error!("Passkey cannot be empty, try again.");
error!("Passkey cannot be empty, try again."); continue;
continue;
}
} }
let passkey_confirm = let passkey_confirm =
rpassword::prompt_password_stdout("Confirm encryption passkey: ").ok(); rpassword::prompt_password_stdout("Confirm encryption passkey: ")
if passkey == passkey_confirm { .map(SecretString::new)?;
if passkey1.expose_secret() == passkey_confirm.expose_secret() {
passkey = Some(passkey1);
break; break;
} }
error!("Passkeys do not match, try again."); error!("Passkeys do not match, try again.");
} }
let passkey = passkey.map(SecretString::new);
#[cfg(feature = "keyring")] #[cfg(feature = "keyring")]
{ {

View file

@ -1,6 +1,7 @@
use std::io::Write; use std::io::Write;
use log::*; use log::*;
use secrecy::{ExposeSecret, SecretString};
use steamguard::{ use steamguard::{
protobufs::steammessages_auth_steamclient::{EAuthSessionGuardType, EAuthTokenPlatformType}, protobufs::steammessages_auth_steamclient::{EAuthSessionGuardType, EAuthTokenPlatformType},
refresher::TokenRefresher, refresher::TokenRefresher,
@ -42,8 +43,8 @@ pub fn do_login<T: Transport + Clone>(
account.account_name = tui::prompt(); account.account_name = tui::prompt();
} }
let _ = std::io::stdout().flush(); let _ = std::io::stdout().flush();
let password = rpassword::prompt_password_stdout("Password: ").unwrap(); let password = tui::prompt_password()?;
if !password.is_empty() { if !password.expose_secret().is_empty() {
debug!("password is present"); debug!("password is present");
} else { } else {
debug!("password is empty"); debug!("password is empty");
@ -65,8 +66,8 @@ pub fn do_login_raw<T: Transport + Clone>(
username: String, username: String,
) -> anyhow::Result<Tokens> { ) -> anyhow::Result<Tokens> {
let _ = std::io::stdout().flush(); let _ = std::io::stdout().flush();
let password = rpassword::prompt_password_stdout("Password: ").unwrap(); let password = tui::prompt_password()?;
if !password.is_empty() { if !password.expose_secret().is_empty() {
debug!("password is present"); debug!("password is present");
} else { } else {
debug!("password is empty"); debug!("password is empty");
@ -77,7 +78,7 @@ pub fn do_login_raw<T: Transport + Clone>(
fn do_login_impl<T: Transport + Clone>( fn do_login_impl<T: Transport + Clone>(
transport: T, transport: T,
username: String, username: String,
password: String, password: SecretString,
account: Option<&SteamGuardAccount>, account: Option<&SteamGuardAccount>,
) -> anyhow::Result<Tokens> { ) -> anyhow::Result<Tokens> {
let mut login = UserLogin::new(transport.clone(), build_device_details()); let mut login = UserLogin::new(transport.clone(), build_device_details());
@ -85,7 +86,7 @@ fn do_login_impl<T: Transport + Clone>(
let mut password = password; let mut password = password;
let confirmation_methods; let confirmation_methods;
loop { loop {
match login.begin_auth_via_credentials(&username, &password) { match login.begin_auth_via_credentials(&username, password.expose_secret()) {
Ok(methods) => { Ok(methods) => {
confirmation_methods = methods; confirmation_methods = methods;
break; break;
@ -95,11 +96,8 @@ fn do_login_impl<T: Transport + Clone>(
return Err(LoginError::TooManyAttempts.into()); return Err(LoginError::TooManyAttempts.into());
} }
Err(LoginError::BadCredentials) => { Err(LoginError::BadCredentials) => {
error!("Incorrect password."); error!("Incorrect password for {username}");
password = rpassword::prompt_password_stdout("Password: ") password = tui::prompt_password()?;
.unwrap()
.trim()
.to_owned();
continue; continue;
} }
Err(err) => { Err(err) => {

View file

@ -152,9 +152,7 @@ fn run(args: commands::Args) -> anyhow::Result<()> {
} }
} }
let raw = passkey = Some(tui::prompt_passkey()?);
rpassword::prompt_password_stdout("Enter encryption passkey: ")?;
passkey = Some(SecretString::new(raw));
} }
Err(e) => { Err(e) => {
error!("Failed to migrate manifest: {}", e); error!("Failed to migrate manifest: {}", e);
@ -208,8 +206,7 @@ fn run(args: commands::Args) -> anyhow::Result<()> {
if manager.has_passkey() { if manager.has_passkey() {
error!("Incorrect passkey"); error!("Incorrect passkey");
} }
let raw = rpassword::prompt_password_stdout("Enter encryption passkey: ")?; passkey = Some(tui::prompt_passkey()?);
passkey = Some(SecretString::new(raw));
manager.submit_passkey(passkey); manager.submit_passkey(passkey);
} }
Err(e) => { Err(e) => {
@ -253,8 +250,7 @@ fn run(args: commands::Args) -> anyhow::Result<()> {
if manager.has_passkey() { if manager.has_passkey() {
error!("Incorrect passkey"); error!("Incorrect passkey");
} }
let raw = rpassword::prompt_password_stdout("Enter encryption passkey: ")?; passkey = Some(tui::prompt_passkey()?);
passkey = Some(SecretString::new(raw));
manager.submit_passkey(passkey); manager.submit_passkey(passkey);
} }
Err(e) => { Err(e) => {

View file

@ -6,6 +6,7 @@ use crossterm::{
terminal::{Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen}, terminal::{Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen},
QueueableCommand, QueueableCommand,
}; };
use secrecy::SecretString;
use std::collections::HashSet; use std::collections::HashSet;
use std::io::{stderr, stdout, Write}; use std::io::{stderr, stdout, Write};
use steamguard::Confirmation; use steamguard::Confirmation;
@ -244,6 +245,24 @@ pub(crate) fn pause() {
} }
} }
pub(crate) fn prompt_passkey() -> std::io::Result<SecretString> {
loop {
let raw = rpassword::prompt_password_stdout("Enter encryption passkey: ")?;
if !raw.is_empty() {
return Ok(SecretString::new(raw));
}
}
}
pub(crate) fn prompt_password() -> std::io::Result<SecretString> {
loop {
let raw = rpassword::prompt_password_stdout("Password: ")?;
if !raw.is_empty() {
return Ok(SecretString::new(raw));
}
}
}
#[cfg(test)] #[cfg(test)]
mod prompt_char_tests { mod prompt_char_tests {
use super::*; use super::*;