mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-08 23:38:49 +00:00
Merge pull request #474 from PR0M3TH3AN/codex/improve-wrong-password-error-message
Improve password retry experience
This commit is contained in:
@@ -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
|
||||||
|
@@ -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(
|
||||||
|
Reference in New Issue
Block a user