diff --git a/src/main.py b/src/main.py index 3b06edc..0415ddd 100644 --- a/src/main.py +++ b/src/main.py @@ -20,7 +20,13 @@ from nostr.client import NostrClient from password_manager.entry_types import EntryType from constants import INACTIVITY_TIMEOUT, initialize_app from utils.password_prompt import PasswordPromptError -from utils import timed_input, copy_to_clipboard, clear_screen, pause +from utils import ( + timed_input, + copy_to_clipboard, + clear_screen, + pause, + clear_and_print_fingerprint, +) from local_bip85.bip85 import Bip85Error @@ -571,7 +577,9 @@ def handle_toggle_secret_mode(pm: PasswordManager) -> None: def handle_profiles_menu(password_manager: PasswordManager) -> None: """Submenu for managing seed profiles.""" while True: - clear_screen() + clear_and_print_fingerprint( + getattr(password_manager, "current_fingerprint", None) + ) print(color_text("\nProfiles:", "menu")) print(color_text("1. Switch Seed Profile", "menu")) print(color_text("2. Add a New Seed Profile", "menu")) @@ -607,7 +615,9 @@ def handle_nostr_menu(password_manager: PasswordManager) -> None: return while True: - clear_screen() + clear_and_print_fingerprint( + getattr(password_manager, "current_fingerprint", None) + ) print(color_text("\nNostr Settings:", "menu")) print(color_text("1. Backup to Nostr", "menu")) print(color_text("2. Restore from Nostr", "menu")) @@ -641,7 +651,9 @@ def handle_nostr_menu(password_manager: PasswordManager) -> None: def handle_settings(password_manager: PasswordManager) -> None: """Interactive settings menu with submenus for profiles and Nostr.""" while True: - clear_screen() + clear_and_print_fingerprint( + getattr(password_manager, "current_fingerprint", None) + ) print(color_text("\nSettings:", "menu")) print(color_text("1. Profiles", "menu")) print(color_text("2. Nostr", "menu")) @@ -733,7 +745,9 @@ def display_menu( display_fn() pause() while True: - clear_screen() + clear_and_print_fingerprint( + getattr(password_manager, "current_fingerprint", None) + ) if time.time() - password_manager.last_activity > inactivity_timeout: print(colored("Session timed out. Vault locked.", "yellow")) password_manager.lock_vault() @@ -805,7 +819,9 @@ def display_menu( elif choice == "2": password_manager.update_activity() password_manager.handle_retrieve_entry() - clear_screen() + clear_and_print_fingerprint( + getattr(password_manager, "current_fingerprint", None) + ) elif choice == "3": password_manager.update_activity() password_manager.handle_search_entries() diff --git a/src/password_manager/manager.py b/src/password_manager/manager.py index a0776e1..36112bb 100644 --- a/src/password_manager/manager.py +++ b/src/password_manager/manager.py @@ -52,7 +52,11 @@ from utils.password_prompt import ( ) from utils.memory_protection import InMemorySecret from utils.clipboard import copy_to_clipboard -from utils.terminal_utils import clear_screen, pause +from utils.terminal_utils import ( + clear_screen, + pause, + clear_and_print_fingerprint, +) from constants import MIN_HEALTHY_RELAYS from constants import ( @@ -886,7 +890,7 @@ class PasswordManager: def handle_add_password(self) -> None: try: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) website_name = input("Enter the label or website name: ").strip() if not website_name: print(colored("Error: Label cannot be empty.", "red")) @@ -973,7 +977,7 @@ class PasswordManager: def handle_add_totp(self) -> None: """Add a TOTP entry either derived from the seed or imported.""" try: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) while True: print("\nAdd TOTP:") print("1. Make 2FA (derive from seed)") @@ -1073,7 +1077,7 @@ class PasswordManager: def handle_add_ssh_key(self) -> None: """Add an SSH key pair entry and display the derived keys.""" try: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) label = input("Label: ").strip() if not label: print(colored("Error: Label cannot be empty.", "red")) @@ -1115,7 +1119,7 @@ class PasswordManager: def handle_add_seed(self) -> None: """Add a derived BIP-39 seed phrase entry.""" try: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) label = input("Label: ").strip() if not label: print(colored("Error: Label cannot be empty.", "red")) @@ -1171,7 +1175,7 @@ class PasswordManager: def handle_add_pgp(self) -> None: """Add a PGP key entry and display the generated key.""" try: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) label = input("Label: ").strip() if not label: print(colored("Error: Label cannot be empty.", "red")) @@ -1224,7 +1228,7 @@ class PasswordManager: def handle_add_nostr_key(self) -> None: """Add a Nostr key entry and display the derived keys.""" try: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) label = input("Label: ").strip() if not label: print(colored("Error: Label cannot be empty.", "red")) @@ -1291,7 +1295,7 @@ class PasswordManager: and displaying the corresponding password and associated details. """ try: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) index_input = input( "Enter the index number of the entry to retrieve: " ).strip() @@ -1609,7 +1613,7 @@ class PasswordManager: and new details to update. """ try: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) index_input = input( "Enter the index number of the entry to modify: " ).strip() @@ -1847,7 +1851,7 @@ class PasswordManager: def handle_search_entries(self) -> None: """Prompt for a query, list matches and optionally show details.""" try: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) query = input("Enter search string: ").strip() if not query: print(colored("No search string provided.", "yellow")) @@ -1861,7 +1865,7 @@ class PasswordManager: return while True: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) print(colored("\n[+] Search Results:\n", "green")) for idx, label, username, _url, _b in results: display_label = label @@ -1968,7 +1972,7 @@ class PasswordManager: """List entries and optionally show details.""" try: while True: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) print(color_text("\nList Entries:", "menu")) print(color_text("1. All", "menu")) print(color_text("2. Passwords", "menu")) @@ -2002,7 +2006,9 @@ class PasswordManager: if not summaries: continue while True: - clear_screen() + clear_and_print_fingerprint( + getattr(self, "current_fingerprint", None) + ) print(colored("\n[+] Entries:\n", "green")) for idx, etype, label in summaries: if filter_kind is None: @@ -2082,7 +2088,7 @@ class PasswordManager: totp_list.sort(key=lambda t: t[0].lower()) print(colored("Press 'b' then Enter to return to the menu.", "cyan")) while True: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) print(colored("Press 'b' then Enter to return to the menu.", "cyan")) generated = [t for t in totp_list if not t[3]] imported_list = [t for t in totp_list if t[3]] @@ -2137,7 +2143,7 @@ class PasswordManager: Handles verifying the script's checksum against the stored checksum to ensure integrity. """ try: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) current_checksum = calculate_checksum(__file__) try: verified = verify_checksum(current_checksum, SCRIPT_CHECKSUM_FILE) @@ -2172,7 +2178,7 @@ class PasswordManager: print(colored("Operation cancelled.", "yellow")) return try: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) script_path = Path(__file__).resolve() if update_checksum_file(str(script_path), str(SCRIPT_CHECKSUM_FILE)): print( @@ -2282,7 +2288,7 @@ class PasswordManager: ) -> Path | None: """Export the current database to an encrypted portable file.""" try: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) path = export_backup( self.vault, self.backup_manager, @@ -2299,7 +2305,7 @@ class PasswordManager: def handle_import_database(self, src: Path) -> None: """Import a portable database file, replacing the current index.""" try: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) import_backup( self.vault, self.backup_manager, @@ -2314,7 +2320,7 @@ class PasswordManager: def handle_export_totp_codes(self) -> Path | None: """Export all 2FA codes to a JSON file for other authenticator apps.""" try: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) data = self.entry_manager.vault.load_index() entries = data.get("entries", {}) @@ -2374,7 +2380,7 @@ class PasswordManager: Handles the backup and reveal of the parent seed. """ try: - clear_screen() + clear_and_print_fingerprint(getattr(self, "current_fingerprint", None)) print(colored("\n=== Backup Parent Seed ===", "yellow")) print( colored( diff --git a/src/utils/__init__.py b/src/utils/__init__.py index 8df494f..25a2ca0 100644 --- a/src/utils/__init__.py +++ b/src/utils/__init__.py @@ -28,7 +28,7 @@ try: from .input_utils import timed_input from .memory_protection import InMemorySecret from .clipboard import copy_to_clipboard - from .terminal_utils import clear_screen, pause + from .terminal_utils import clear_screen, pause, clear_and_print_fingerprint if logger.isEnabledFor(logging.DEBUG): logger.info("Modules imported successfully.") @@ -57,5 +57,6 @@ __all__ = [ "InMemorySecret", "copy_to_clipboard", "clear_screen", + "clear_and_print_fingerprint", "pause", ] diff --git a/src/utils/terminal_utils.py b/src/utils/terminal_utils.py index 563e0fe..a520074 100644 --- a/src/utils/terminal_utils.py +++ b/src/utils/terminal_utils.py @@ -3,11 +3,21 @@ import sys +from termcolor import colored + + def clear_screen() -> None: """Clear the terminal screen using an ANSI escape code.""" print("\033c", end="") +def clear_and_print_fingerprint(fingerprint: str | None) -> None: + """Clear the screen and optionally display the current fingerprint.""" + clear_screen() + if fingerprint: + print(colored(f"Seed Profile: {fingerprint}", "green")) + + def pause(message: str = "Press Enter to continue...") -> None: """Wait for the user to press Enter before proceeding.""" if not sys.stdin or not sys.stdin.isatty():