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,51 +377,61 @@ 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."""
try: attempts = 0
if password is None: max_attempts = 5
password = prompt_existing_password("Enter your master password: ") while attempts < max_attempts:
iterations = (
self.config_manager.get_kdf_iterations()
if getattr(self, "config_manager", None)
else 100_000
)
print("Deriving key...")
seed_key = derive_key_from_password(password, iterations=iterations)
seed_mgr = EncryptionManager(seed_key, fingerprint_dir)
print("Decrypting seed...")
try: try:
self.parent_seed = seed_mgr.decrypt_parent_seed() if password is None:
except Exception: password = prompt_existing_password("Enter your master password: ")
msg = "Invalid password for selected seed profile."
print(colored(msg, "red")) iterations = (
self.config_manager.get_kdf_iterations()
if getattr(self, "config_manager", None)
else 100_000
)
print("Deriving key...")
seed_key = derive_key_from_password(password, iterations=iterations)
seed_mgr = EncryptionManager(seed_key, fingerprint_dir)
print("Decrypting seed...")
try:
self.parent_seed = seed_mgr.decrypt_parent_seed()
except Exception:
msg = (
"Invalid password for selected seed profile. Please try again."
)
print(colored(msg, "red"))
attempts += 1
password = None
continue
key = derive_index_key(self.parent_seed)
self.encryption_manager = EncryptionManager(key, fingerprint_dir)
self.vault = Vault(self.encryption_manager, fingerprint_dir)
self.config_manager = ConfigManager(
vault=self.vault,
fingerprint_dir=fingerprint_dir,
)
self.fingerprint_dir = fingerprint_dir
if not self.verify_password(password):
print(colored("Invalid password. Please try again.", "red"))
attempts += 1
password = None
continue
return True
except KeyboardInterrupt:
raise
except Exception as e:
logger.error(f"Failed to set up EncryptionManager: {e}", exc_info=True)
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:
key = derive_index_key(self.parent_seed) sys.exit(1)
return False
self.encryption_manager = EncryptionManager(key, fingerprint_dir)
self.vault = Vault(self.encryption_manager, fingerprint_dir)
self.config_manager = ConfigManager(
vault=self.vault,
fingerprint_dir=fingerprint_dir,
)
self.fingerprint_dir = fingerprint_dir
if not self.verify_password(password):
print(colored("Invalid password.", "red"))
if exit_on_fail:
sys.exit(1)
return False
return True
except Exception as e:
logger.error(f"Failed to set up EncryptionManager: {e}", exc_info=True)
print(colored(f"Error: Failed to set up encryption: {e}", "red"))
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,44 +106,55 @@ 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.
""" """
try: attempts = 0
password = getpass.getpass(prompt=prompt_message).strip() while attempts < max_retries:
try:
password = getpass.getpass(prompt=prompt_message).strip()
if not password: if not password:
print(colored("Error: Password cannot be empty.", "red")) print(
logging.warning("User attempted to enter an empty password.") colored("Error: Password cannot be empty. Please try again.", "red")
raise PasswordPromptError("Password cannot be empty") )
logging.warning("User attempted to enter an empty password.")
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
except KeyboardInterrupt: except KeyboardInterrupt:
print(colored("\nOperation cancelled by user.", "yellow")) print(colored("\nOperation cancelled by user.", "yellow"))
logging.info("Existing password prompt interrupted by user.") logging.info("Existing password prompt interrupted by user.")
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")) )
raise PasswordPromptError(str(e)) print(colored(f"Error: {e}", "red"))
attempts += 1
raise PasswordPromptError("Maximum password attempts exceeded")
def confirm_action( def confirm_action(