Improve password retry flow

This commit is contained in:
thePR0M3TH3AN
2025-07-12 09:37:42 -04:00
parent 0ef3c2207f
commit 496950c501
2 changed files with 88 additions and 67 deletions

View File

@@ -377,6 +377,9 @@ class PasswordManager:
) -> bool: ) -> bool:
"""Set up encryption for the current fingerprint and load the seed.""" """Set up encryption for the current fingerprint and load the seed."""
attempts = 0
max_attempts = 5
while attempts < max_attempts:
try: try:
if password is None: if password is None:
password = prompt_existing_password("Enter your master password: ") password = prompt_existing_password("Enter your master password: ")
@@ -393,11 +396,13 @@ class PasswordManager:
try: try:
self.parent_seed = seed_mgr.decrypt_parent_seed() self.parent_seed = seed_mgr.decrypt_parent_seed()
except Exception: except Exception:
msg = "Invalid password for selected seed profile." msg = (
"Invalid password for selected seed profile. Please try again."
)
print(colored(msg, "red")) print(colored(msg, "red"))
if exit_on_fail: attempts += 1
sys.exit(1) password = None
return False continue
key = derive_index_key(self.parent_seed) key = derive_index_key(self.parent_seed)
@@ -411,17 +416,22 @@ class PasswordManager:
self.fingerprint_dir = fingerprint_dir self.fingerprint_dir = fingerprint_dir
if not self.verify_password(password): if not self.verify_password(password):
print(colored("Invalid password.", "red")) print(colored("Invalid password. Please try again.", "red"))
if exit_on_fail: attempts += 1
sys.exit(1) password = None
return False continue
return True return True
except KeyboardInterrupt:
raise
except Exception as e: except Exception as e:
logger.error(f"Failed to set up EncryptionManager: {e}", exc_info=True) logger.error(f"Failed to set up EncryptionManager: {e}", exc_info=True)
print(colored(f"Error: Failed to set up encryption: {e}", "red")) print(colored(f"Error: Failed to set up encryption: {e}", "red"))
if exit_on_fail: if exit_on_fail:
sys.exit(1) sys.exit(1)
return False return False
if exit_on_fail:
sys.exit(1)
return False
def load_parent_seed( def load_parent_seed(
self, fingerprint_dir: Path, password: Optional[str] = None self, fingerprint_dir: Path, password: Optional[str] = None

View File

@@ -106,30 +106,38 @@ def prompt_new_password() -> str:
raise PasswordPromptError("Maximum password attempts exceeded") raise PasswordPromptError("Maximum password attempts exceeded")
def prompt_existing_password(prompt_message: str = "Enter your password: ") -> str: def prompt_existing_password(
prompt_message: str = "Enter your password: ", max_retries: int = 5
) -> str:
""" """
Prompts the user to enter an existing password, typically used for decryption purposes. Prompt the user for an existing password.
This function ensures that the password is entered securely without echoing it to the terminal. The user will be reprompted on empty input up to ``max_retries`` times.
Parameters: Parameters:
prompt_message (str): The message displayed to prompt the user. Defaults to "Enter your password: ". prompt_message (str): Message displayed when prompting for the password.
max_retries (int): Number of attempts allowed before aborting.
Returns: Returns:
str: The password entered by the user. str: The password provided by the user.
Raises: Raises:
PasswordPromptError: If the user interrupts the operation. PasswordPromptError: If the user interrupts the operation or exceeds
``max_retries`` attempts.
""" """
attempts = 0
while attempts < max_retries:
try: try:
password = getpass.getpass(prompt=prompt_message).strip() password = getpass.getpass(prompt=prompt_message).strip()
if not password: if not password:
print(colored("Error: Password cannot be empty.", "red")) print(
colored("Error: Password cannot be empty. Please try again.", "red")
)
logging.warning("User attempted to enter an empty password.") logging.warning("User attempted to enter an empty password.")
raise PasswordPromptError("Password cannot be empty") attempts += 1
continue
# Normalize the password to NFKD form
normalized_password = unicodedata.normalize("NFKD", password) normalized_password = unicodedata.normalize("NFKD", password)
logging.debug("User entered an existing password for decryption.") logging.debug("User entered an existing password for decryption.")
return normalized_password return normalized_password
@@ -140,10 +148,13 @@ def prompt_existing_password(prompt_message: str = "Enter your password: ") -> s
raise PasswordPromptError("Operation cancelled by user") raise PasswordPromptError("Operation cancelled by user")
except Exception as e: except Exception as e:
logging.error( logging.error(
f"Unexpected error during existing password prompt: {e}", exc_info=True f"Unexpected error during existing password prompt: {e}",
exc_info=True,
) )
print(colored(f"Error: {e}", "red")) print(colored(f"Error: {e}", "red"))
raise PasswordPromptError(str(e)) attempts += 1
raise PasswordPromptError("Maximum password attempts exceeded")
def confirm_action( def confirm_action(