mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-08 07:18:47 +00:00
refactor: extract sensitive entry display helper
This commit is contained in:
@@ -153,6 +153,7 @@ class PasswordManager:
|
||||
self.profile_stack: list[tuple[str, Path, str]] = []
|
||||
self.last_unlock_duration: float | None = None
|
||||
self.verbose_timing: bool = False
|
||||
self._suppress_entry_actions_menu: bool = False
|
||||
|
||||
# Initialize the fingerprint manager first
|
||||
self.initialize_fingerprint_manager()
|
||||
@@ -2053,11 +2054,422 @@ class PasswordManager:
|
||||
logging.error(f"Error displaying QR menu: {e}", exc_info=True)
|
||||
print(colored(f"Error: Failed to display QR codes: {e}", "red"))
|
||||
|
||||
def display_sensitive_entry_info(self, entry: dict, index: int) -> None:
|
||||
"""Display information for a sensitive entry.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
entry: dict
|
||||
Entry data retrieved from the vault.
|
||||
index: int
|
||||
Index of the entry being displayed.
|
||||
"""
|
||||
|
||||
self._suppress_entry_actions_menu = False
|
||||
|
||||
entry_type = entry.get("type", entry.get("kind", EntryType.PASSWORD.value))
|
||||
if isinstance(entry_type, str):
|
||||
entry_type = entry_type.lower()
|
||||
|
||||
if entry_type == EntryType.TOTP.value:
|
||||
label = entry.get("label", "")
|
||||
period = int(entry.get("period", 30))
|
||||
notes = entry.get("notes", "")
|
||||
print(colored(f"Retrieving 2FA code for '{label}'.", "cyan"))
|
||||
print(colored("Press Enter to return to the menu.", "cyan"))
|
||||
try:
|
||||
while True:
|
||||
code = self.entry_manager.get_totp_code(index, self.parent_seed)
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(code, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] 2FA code for '{label}' copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(colored("\n[+] Retrieved 2FA Code:\n", "green"))
|
||||
print(colored(f"Label: {label}", "cyan"))
|
||||
imported = "secret" in entry
|
||||
category = "imported" if imported else "deterministic"
|
||||
print(color_text(f"Code: {code}", category))
|
||||
if notes:
|
||||
print(colored(f"Notes: {notes}", "cyan"))
|
||||
tags = entry.get("tags", [])
|
||||
if tags:
|
||||
print(colored(f"Tags: {', '.join(tags)}", "cyan"))
|
||||
remaining = self.entry_manager.get_totp_time_remaining(index)
|
||||
exit_loop = False
|
||||
while remaining > 0:
|
||||
filled = int(20 * (period - remaining) / period)
|
||||
bar = "[" + "#" * filled + "-" * (20 - filled) + "]"
|
||||
sys.stdout.write(f"\r{bar} {remaining:2d}s")
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
user_input = timed_input("", 1)
|
||||
if (
|
||||
user_input.strip() == ""
|
||||
or user_input.strip().lower() == "b"
|
||||
):
|
||||
exit_loop = True
|
||||
break
|
||||
except TimeoutError:
|
||||
pass
|
||||
except KeyboardInterrupt:
|
||||
exit_loop = True
|
||||
print()
|
||||
break
|
||||
remaining -= 1
|
||||
sys.stdout.write("\n")
|
||||
sys.stdout.flush()
|
||||
if exit_loop:
|
||||
break
|
||||
except Exception as e: # pragma: no cover - best effort
|
||||
logging.error(f"Error generating TOTP code: {e}", exc_info=True)
|
||||
print(colored(f"Error: Failed to generate TOTP code: {e}", "red"))
|
||||
return
|
||||
|
||||
if entry_type == EntryType.SSH.value:
|
||||
notes = entry.get("notes", "")
|
||||
label = entry.get("label", "")
|
||||
if not confirm_action(
|
||||
"WARNING: Displaying SSH keys reveals sensitive information. Continue? (Y/N): "
|
||||
):
|
||||
self.notify("SSH key display cancelled.", level="WARNING")
|
||||
return
|
||||
try:
|
||||
priv_pem, pub_pem = self.entry_manager.get_ssh_key_pair(
|
||||
index, self.parent_seed
|
||||
)
|
||||
print(colored("\n[+] Retrieved SSH Key Pair:\n", "green"))
|
||||
if label:
|
||||
print(colored(f"Label: {label}", "cyan"))
|
||||
if notes:
|
||||
print(colored(f"Notes: {notes}", "cyan"))
|
||||
tags = entry.get("tags", [])
|
||||
if tags:
|
||||
print(colored(f"Tags: {', '.join(tags)}", "cyan"))
|
||||
print(colored("Public Key:", "cyan"))
|
||||
print(color_text(pub_pem, "default"))
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(priv_pem, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] SSH private key copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(colored("Private Key:", "cyan"))
|
||||
print(color_text(priv_pem, "deterministic"))
|
||||
except Exception as e: # pragma: no cover - best effort
|
||||
logging.error(f"Error deriving SSH key pair: {e}", exc_info=True)
|
||||
print(colored(f"Error: Failed to derive SSH keys: {e}", "red"))
|
||||
return
|
||||
|
||||
if entry_type == EntryType.SEED.value:
|
||||
notes = entry.get("notes", "")
|
||||
label = entry.get("label", "")
|
||||
if not confirm_action(
|
||||
"WARNING: Displaying the seed phrase reveals sensitive information. Continue? (Y/N): "
|
||||
):
|
||||
self.notify("Seed phrase display cancelled.", level="WARNING")
|
||||
return
|
||||
try:
|
||||
phrase = self.entry_manager.get_seed_phrase(index, self.parent_seed)
|
||||
print(colored("\n[+] Retrieved Seed Phrase:\n", "green"))
|
||||
print(colored(f"Index: {index}", "cyan"))
|
||||
if label:
|
||||
print(colored(f"Label: {label}", "cyan"))
|
||||
if notes:
|
||||
print(colored(f"Notes: {notes}", "cyan"))
|
||||
tags = entry.get("tags", [])
|
||||
if tags:
|
||||
print(colored(f"Tags: {', '.join(tags)}", "cyan"))
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(phrase, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] Seed phrase copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(color_text(phrase, "deterministic"))
|
||||
if confirm_action("Show derived entropy as hex? (Y/N): "):
|
||||
from local_bip85.bip85 import BIP85
|
||||
from bip_utils import Bip39SeedGenerator
|
||||
|
||||
words = int(entry.get("word_count", entry.get("words", 24)))
|
||||
bytes_len = {12: 16, 18: 24, 24: 32}.get(words, 32)
|
||||
seed_bytes = Bip39SeedGenerator(self.parent_seed).Generate()
|
||||
bip85 = BIP85(seed_bytes)
|
||||
entropy = bip85.derive_entropy(
|
||||
index=int(entry.get("index", index)),
|
||||
bytes_len=bytes_len,
|
||||
app_no=39,
|
||||
words_len=words,
|
||||
)
|
||||
print(color_text(f"Entropy: {entropy.hex()}", "deterministic"))
|
||||
except Exception as e: # pragma: no cover - best effort
|
||||
logging.error(f"Error deriving seed phrase: {e}", exc_info=True)
|
||||
print(colored(f"Error: Failed to derive seed phrase: {e}", "red"))
|
||||
return
|
||||
|
||||
if entry_type == EntryType.PGP.value:
|
||||
notes = entry.get("notes", "")
|
||||
label = entry.get("user_id", "")
|
||||
if not confirm_action(
|
||||
"WARNING: Displaying the PGP key reveals sensitive information. Continue? (Y/N): "
|
||||
):
|
||||
self.notify("PGP key display cancelled.", level="WARNING")
|
||||
return
|
||||
try:
|
||||
priv_key, fingerprint = self.entry_manager.get_pgp_key(
|
||||
index, self.parent_seed
|
||||
)
|
||||
print(colored("\n[+] Retrieved PGP Key:\n", "green"))
|
||||
if label:
|
||||
print(colored(f"User ID: {label}", "cyan"))
|
||||
if notes:
|
||||
print(colored(f"Notes: {notes}", "cyan"))
|
||||
tags = entry.get("tags", [])
|
||||
if tags:
|
||||
print(colored(f"Tags: {', '.join(tags)}", "cyan"))
|
||||
print(colored(f"Fingerprint: {fingerprint}", "cyan"))
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(priv_key, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] PGP key copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(color_text(priv_key, "deterministic"))
|
||||
except Exception as e: # pragma: no cover - best effort
|
||||
logging.error(f"Error deriving PGP key: {e}", exc_info=True)
|
||||
print(colored(f"Error: Failed to derive PGP key: {e}", "red"))
|
||||
return
|
||||
|
||||
if entry_type == EntryType.NOSTR.value:
|
||||
label = entry.get("label", "")
|
||||
notes = entry.get("notes", "")
|
||||
try:
|
||||
npub, nsec = self.entry_manager.get_nostr_key_pair(
|
||||
index, self.parent_seed
|
||||
)
|
||||
print(colored("\n[+] Retrieved Nostr Keys:\n", "green"))
|
||||
print(colored(f"Label: {label}", "cyan"))
|
||||
print(colored(f"npub: {npub}", "cyan"))
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(nsec, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] nsec copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(color_text(f"nsec: {nsec}", "deterministic"))
|
||||
if notes:
|
||||
print(colored(f"Notes: {notes}", "cyan"))
|
||||
tags = entry.get("tags", [])
|
||||
if tags:
|
||||
print(colored(f"Tags: {', '.join(tags)}", "cyan"))
|
||||
except Exception as e: # pragma: no cover - best effort
|
||||
logging.error(f"Error deriving Nostr keys: {e}", exc_info=True)
|
||||
print(colored(f"Error: Failed to derive Nostr keys: {e}", "red"))
|
||||
return
|
||||
|
||||
if entry_type == EntryType.KEY_VALUE.value:
|
||||
label = entry.get("label", "")
|
||||
value = entry.get("value", "")
|
||||
notes = entry.get("notes", "")
|
||||
archived = entry.get("archived", False)
|
||||
print(colored(f"Retrieving value for '{label}'.", "cyan"))
|
||||
if notes:
|
||||
print(colored(f"Notes: {notes}", "cyan"))
|
||||
tags = entry.get("tags", [])
|
||||
if tags:
|
||||
print(colored(f"Tags: {', '.join(tags)}", "cyan"))
|
||||
print(
|
||||
colored(
|
||||
f"Archived Status: {'Archived' if archived else 'Active'}", "cyan"
|
||||
)
|
||||
)
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(value, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] Value copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(color_text(f"Value: {value}", "deterministic"))
|
||||
|
||||
custom_fields = entry.get("custom_fields", [])
|
||||
if custom_fields:
|
||||
print(colored("Additional Fields:", "cyan"))
|
||||
hidden_fields = []
|
||||
for field in custom_fields:
|
||||
f_label = field.get("label", "")
|
||||
f_value = field.get("value", "")
|
||||
if field.get("is_hidden"):
|
||||
hidden_fields.append((f_label, f_value))
|
||||
print(colored(f" {f_label}: [hidden]", "cyan"))
|
||||
else:
|
||||
print(colored(f" {f_label}: {f_value}", "cyan"))
|
||||
if hidden_fields:
|
||||
show = input("Reveal hidden fields? (y/N): ").strip().lower()
|
||||
if show == "y":
|
||||
for f_label, f_value in hidden_fields:
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(f_value, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] {f_label} copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(colored(f" {f_label}: {f_value}", "cyan"))
|
||||
return
|
||||
|
||||
if entry_type == EntryType.MANAGED_ACCOUNT.value:
|
||||
label = entry.get("label", "")
|
||||
notes = entry.get("notes", "")
|
||||
archived = entry.get("archived", False)
|
||||
fingerprint = entry.get("fingerprint", "")
|
||||
print(colored(f"Managed account '{label}'.", "cyan"))
|
||||
if notes:
|
||||
print(colored(f"Notes: {notes}", "cyan"))
|
||||
if fingerprint:
|
||||
print(colored(f"Fingerprint: {fingerprint}", "cyan"))
|
||||
tags = entry.get("tags", [])
|
||||
if tags:
|
||||
print(colored(f"Tags: {', '.join(tags)}", "cyan"))
|
||||
print(
|
||||
colored(
|
||||
f"Archived Status: {'Archived' if archived else 'Active'}", "cyan"
|
||||
)
|
||||
)
|
||||
action = (
|
||||
input(
|
||||
"Enter 'r' to reveal seed, 'l' to load account, or press Enter to go back: "
|
||||
)
|
||||
.strip()
|
||||
.lower()
|
||||
)
|
||||
if action == "r":
|
||||
seed = self.entry_manager.get_managed_account_seed(
|
||||
index, self.parent_seed
|
||||
)
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(seed, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] Seed phrase copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(color_text(seed, "deterministic"))
|
||||
return
|
||||
if action == "l":
|
||||
self._suppress_entry_actions_menu = True
|
||||
self.load_managed_account(index)
|
||||
return
|
||||
return
|
||||
|
||||
# Default: PASSWORD
|
||||
website_name = entry.get("label", entry.get("website"))
|
||||
length = entry.get("length")
|
||||
username = entry.get("username")
|
||||
url = entry.get("url")
|
||||
blacklisted = entry.get("archived", entry.get("blacklisted"))
|
||||
notes = entry.get("notes", "")
|
||||
|
||||
print(
|
||||
colored(
|
||||
f"Retrieving password for '{website_name}' with length {length}.",
|
||||
"cyan",
|
||||
)
|
||||
)
|
||||
if username:
|
||||
print(colored(f"Username: {username}", "cyan"))
|
||||
if url:
|
||||
print(colored(f"URL: {url}", "cyan"))
|
||||
if blacklisted:
|
||||
self.notify(
|
||||
"Warning: This password is archived and should not be used.",
|
||||
level="WARNING",
|
||||
)
|
||||
|
||||
password = self.password_generator.generate_password(length, index)
|
||||
|
||||
if password:
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(password, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] Password for '{website_name}' copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(
|
||||
colored(
|
||||
f"\n[+] Retrieved Password for {website_name}:\n",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
print(color_text(f"Password: {password}", "deterministic"))
|
||||
print(colored(f"Associated Username: {username or 'N/A'}", "cyan"))
|
||||
print(colored(f"Associated URL: {url or 'N/A'}", "cyan"))
|
||||
print(
|
||||
colored(
|
||||
f"Archived Status: {'Archived' if blacklisted else 'Active'}",
|
||||
"cyan",
|
||||
)
|
||||
)
|
||||
tags = entry.get("tags", [])
|
||||
if tags:
|
||||
print(colored(f"Tags: {', '.join(tags)}", "cyan"))
|
||||
custom_fields = entry.get("custom_fields", [])
|
||||
if custom_fields:
|
||||
print(colored("Additional Fields:", "cyan"))
|
||||
hidden_fields = []
|
||||
for field in custom_fields:
|
||||
label = field.get("label", "")
|
||||
value = field.get("value", "")
|
||||
if field.get("is_hidden"):
|
||||
hidden_fields.append((label, value))
|
||||
print(colored(f" {label}: [hidden]", "cyan"))
|
||||
else:
|
||||
print(colored(f" {label}: {value}", "cyan"))
|
||||
if hidden_fields:
|
||||
show = input("Reveal hidden fields? (y/N): ").strip().lower()
|
||||
if show == "y":
|
||||
for label, value in hidden_fields:
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(value, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] {label} copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(colored(f" {label}: {value}", "cyan"))
|
||||
else:
|
||||
print(colored("Error: Failed to retrieve the password.", "red"))
|
||||
return
|
||||
|
||||
def handle_retrieve_entry(self) -> None:
|
||||
"""
|
||||
Handles retrieving a password from the index by prompting the user for the index number
|
||||
and displaying the corresponding password and associated details.
|
||||
"""
|
||||
"""Prompt for an index and display the corresponding entry."""
|
||||
try:
|
||||
fp, parent_fp, child_fp = self.header_fingerprint_args
|
||||
clear_header_with_notification(
|
||||
@@ -2081,431 +2493,15 @@ class PasswordManager:
|
||||
pause()
|
||||
return
|
||||
|
||||
entry_type = entry.get("type", entry.get("kind", EntryType.PASSWORD.value))
|
||||
if isinstance(entry_type, str):
|
||||
entry_type = entry_type.lower()
|
||||
self.display_sensitive_entry_info(entry, index)
|
||||
pause()
|
||||
|
||||
if entry_type == EntryType.TOTP.value:
|
||||
label = entry.get("label", "")
|
||||
period = int(entry.get("period", 30))
|
||||
notes = entry.get("notes", "")
|
||||
print(colored(f"Retrieving 2FA code for '{label}'.", "cyan"))
|
||||
print(colored("Press Enter to return to the menu.", "cyan"))
|
||||
try:
|
||||
while True:
|
||||
code = self.entry_manager.get_totp_code(index, self.parent_seed)
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(code, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] 2FA code for '{label}' copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(colored("\n[+] Retrieved 2FA Code:\n", "green"))
|
||||
print(colored(f"Label: {label}", "cyan"))
|
||||
imported = "secret" in entry
|
||||
category = "imported" if imported else "deterministic"
|
||||
print(color_text(f"Code: {code}", category))
|
||||
if notes:
|
||||
print(colored(f"Notes: {notes}", "cyan"))
|
||||
tags = entry.get("tags", [])
|
||||
if tags:
|
||||
print(colored(f"Tags: {', '.join(tags)}", "cyan"))
|
||||
remaining = self.entry_manager.get_totp_time_remaining(index)
|
||||
exit_loop = False
|
||||
while remaining > 0:
|
||||
filled = int(20 * (period - remaining) / period)
|
||||
bar = "[" + "#" * filled + "-" * (20 - filled) + "]"
|
||||
sys.stdout.write(f"\r{bar} {remaining:2d}s")
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
user_input = timed_input("", 1)
|
||||
if (
|
||||
user_input.strip() == ""
|
||||
or user_input.strip().lower() == "b"
|
||||
):
|
||||
exit_loop = True
|
||||
break
|
||||
except TimeoutError:
|
||||
pass
|
||||
except KeyboardInterrupt:
|
||||
exit_loop = True
|
||||
print()
|
||||
break
|
||||
remaining -= 1
|
||||
sys.stdout.write("\n")
|
||||
sys.stdout.flush()
|
||||
if exit_loop:
|
||||
break
|
||||
except Exception as e:
|
||||
logging.error(f"Error generating TOTP code: {e}", exc_info=True)
|
||||
print(colored(f"Error: Failed to generate TOTP code: {e}", "red"))
|
||||
self._entry_actions_menu(index, entry)
|
||||
pause()
|
||||
return
|
||||
if entry_type == EntryType.SSH.value:
|
||||
notes = entry.get("notes", "")
|
||||
label = entry.get("label", "")
|
||||
if not confirm_action(
|
||||
"WARNING: Displaying SSH keys reveals sensitive information. Continue? (Y/N): "
|
||||
):
|
||||
self.notify("SSH key display cancelled.", level="WARNING")
|
||||
return
|
||||
try:
|
||||
priv_pem, pub_pem = self.entry_manager.get_ssh_key_pair(
|
||||
index, self.parent_seed
|
||||
)
|
||||
print(colored("\n[+] Retrieved SSH Key Pair:\n", "green"))
|
||||
if label:
|
||||
print(colored(f"Label: {label}", "cyan"))
|
||||
if notes:
|
||||
print(colored(f"Notes: {notes}", "cyan"))
|
||||
tags = entry.get("tags", [])
|
||||
if tags:
|
||||
print(colored(f"Tags: {', '.join(tags)}", "cyan"))
|
||||
print(colored("Public Key:", "cyan"))
|
||||
print(color_text(pub_pem, "default"))
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(priv_pem, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] SSH private key copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(colored("Private Key:", "cyan"))
|
||||
print(color_text(priv_pem, "deterministic"))
|
||||
except Exception as e:
|
||||
logging.error(f"Error deriving SSH key pair: {e}", exc_info=True)
|
||||
print(colored(f"Error: Failed to derive SSH keys: {e}", "red"))
|
||||
pause()
|
||||
self._entry_actions_menu(index, entry)
|
||||
pause()
|
||||
return
|
||||
if entry_type == EntryType.SEED.value:
|
||||
notes = entry.get("notes", "")
|
||||
label = entry.get("label", "")
|
||||
if not confirm_action(
|
||||
"WARNING: Displaying the seed phrase reveals sensitive information. Continue? (Y/N): "
|
||||
):
|
||||
self.notify("Seed phrase display cancelled.", level="WARNING")
|
||||
return
|
||||
try:
|
||||
phrase = self.entry_manager.get_seed_phrase(index, self.parent_seed)
|
||||
print(colored("\n[+] Retrieved Seed Phrase:\n", "green"))
|
||||
print(colored(f"Index: {index}", "cyan"))
|
||||
if label:
|
||||
print(colored(f"Label: {label}", "cyan"))
|
||||
if notes:
|
||||
print(colored(f"Notes: {notes}", "cyan"))
|
||||
tags = entry.get("tags", [])
|
||||
if tags:
|
||||
print(colored(f"Tags: {', '.join(tags)}", "cyan"))
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(phrase, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] Seed phrase copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(color_text(phrase, "deterministic"))
|
||||
# Removed QR code display prompt and output
|
||||
if confirm_action("Show derived entropy as hex? (Y/N): "):
|
||||
from local_bip85.bip85 import BIP85
|
||||
from bip_utils import Bip39SeedGenerator
|
||||
|
||||
words = int(entry.get("word_count", entry.get("words", 24)))
|
||||
bytes_len = {12: 16, 18: 24, 24: 32}.get(words, 32)
|
||||
seed_bytes = Bip39SeedGenerator(self.parent_seed).Generate()
|
||||
bip85 = BIP85(seed_bytes)
|
||||
entropy = bip85.derive_entropy(
|
||||
index=int(entry.get("index", index)),
|
||||
bytes_len=bytes_len,
|
||||
app_no=39,
|
||||
words_len=words,
|
||||
)
|
||||
print(color_text(f"Entropy: {entropy.hex()}", "deterministic"))
|
||||
except Exception as e:
|
||||
logging.error(f"Error deriving seed phrase: {e}", exc_info=True)
|
||||
print(colored(f"Error: Failed to derive seed phrase: {e}", "red"))
|
||||
pause()
|
||||
self._entry_actions_menu(index, entry)
|
||||
pause()
|
||||
return
|
||||
if entry_type == EntryType.PGP.value:
|
||||
notes = entry.get("notes", "")
|
||||
label = entry.get("user_id", "")
|
||||
if not confirm_action(
|
||||
"WARNING: Displaying the PGP key reveals sensitive information. Continue? (Y/N): "
|
||||
):
|
||||
self.notify("PGP key display cancelled.", level="WARNING")
|
||||
return
|
||||
try:
|
||||
priv_key, fingerprint = self.entry_manager.get_pgp_key(
|
||||
index, self.parent_seed
|
||||
)
|
||||
print(colored("\n[+] Retrieved PGP Key:\n", "green"))
|
||||
if label:
|
||||
print(colored(f"User ID: {label}", "cyan"))
|
||||
if notes:
|
||||
print(colored(f"Notes: {notes}", "cyan"))
|
||||
tags = entry.get("tags", [])
|
||||
if tags:
|
||||
print(colored(f"Tags: {', '.join(tags)}", "cyan"))
|
||||
print(colored(f"Fingerprint: {fingerprint}", "cyan"))
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(priv_key, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] PGP key copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(color_text(priv_key, "deterministic"))
|
||||
except Exception as e:
|
||||
logging.error(f"Error deriving PGP key: {e}", exc_info=True)
|
||||
print(colored(f"Error: Failed to derive PGP key: {e}", "red"))
|
||||
pause()
|
||||
self._entry_actions_menu(index, entry)
|
||||
pause()
|
||||
return
|
||||
if entry_type == EntryType.NOSTR.value:
|
||||
label = entry.get("label", "")
|
||||
notes = entry.get("notes", "")
|
||||
try:
|
||||
npub, nsec = self.entry_manager.get_nostr_key_pair(
|
||||
index, self.parent_seed
|
||||
)
|
||||
print(colored("\n[+] Retrieved Nostr Keys:\n", "green"))
|
||||
print(colored(f"Label: {label}", "cyan"))
|
||||
print(colored(f"npub: {npub}", "cyan"))
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(nsec, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] nsec copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(color_text(f"nsec: {nsec}", "deterministic"))
|
||||
# QR code display removed for npub and nsec
|
||||
if notes:
|
||||
print(colored(f"Notes: {notes}", "cyan"))
|
||||
tags = entry.get("tags", [])
|
||||
if tags:
|
||||
print(colored(f"Tags: {', '.join(tags)}", "cyan"))
|
||||
except Exception as e:
|
||||
logging.error(f"Error deriving Nostr keys: {e}", exc_info=True)
|
||||
print(colored(f"Error: Failed to derive Nostr keys: {e}", "red"))
|
||||
pause()
|
||||
self._entry_actions_menu(index, entry)
|
||||
pause()
|
||||
if getattr(self, "_suppress_entry_actions_menu", False):
|
||||
return
|
||||
|
||||
if entry_type == EntryType.KEY_VALUE.value:
|
||||
label = entry.get("label", "")
|
||||
value = entry.get("value", "")
|
||||
notes = entry.get("notes", "")
|
||||
archived = entry.get("archived", False)
|
||||
print(colored(f"Retrieving value for '{label}'.", "cyan"))
|
||||
if notes:
|
||||
print(colored(f"Notes: {notes}", "cyan"))
|
||||
tags = entry.get("tags", [])
|
||||
if tags:
|
||||
print(colored(f"Tags: {', '.join(tags)}", "cyan"))
|
||||
print(
|
||||
colored(
|
||||
f"Archived Status: {'Archived' if archived else 'Active'}",
|
||||
"cyan",
|
||||
)
|
||||
)
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(value, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] Value copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(color_text(f"Value: {value}", "deterministic"))
|
||||
|
||||
custom_fields = entry.get("custom_fields", [])
|
||||
if custom_fields:
|
||||
print(colored("Additional Fields:", "cyan"))
|
||||
hidden_fields = []
|
||||
for field in custom_fields:
|
||||
f_label = field.get("label", "")
|
||||
f_value = field.get("value", "")
|
||||
if field.get("is_hidden"):
|
||||
hidden_fields.append((f_label, f_value))
|
||||
print(colored(f" {f_label}: [hidden]", "cyan"))
|
||||
else:
|
||||
print(colored(f" {f_label}: {f_value}", "cyan"))
|
||||
if hidden_fields:
|
||||
show = input("Reveal hidden fields? (y/N): ").strip().lower()
|
||||
if show == "y":
|
||||
for f_label, f_value in hidden_fields:
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(
|
||||
f_value, self.clipboard_clear_delay
|
||||
)
|
||||
print(
|
||||
colored(
|
||||
f"[+] {f_label} copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(colored(f" {f_label}: {f_value}", "cyan"))
|
||||
self._entry_actions_menu(index, entry)
|
||||
pause()
|
||||
return
|
||||
if entry_type == EntryType.MANAGED_ACCOUNT.value:
|
||||
label = entry.get("label", "")
|
||||
notes = entry.get("notes", "")
|
||||
archived = entry.get("archived", False)
|
||||
fingerprint = entry.get("fingerprint", "")
|
||||
print(colored(f"Managed account '{label}'.", "cyan"))
|
||||
if notes:
|
||||
print(colored(f"Notes: {notes}", "cyan"))
|
||||
if fingerprint:
|
||||
print(colored(f"Fingerprint: {fingerprint}", "cyan"))
|
||||
tags = entry.get("tags", [])
|
||||
if tags:
|
||||
print(colored(f"Tags: {', '.join(tags)}", "cyan"))
|
||||
print(
|
||||
colored(
|
||||
f"Archived Status: {'Archived' if archived else 'Active'}",
|
||||
"cyan",
|
||||
)
|
||||
)
|
||||
action = (
|
||||
input(
|
||||
"Enter 'r' to reveal seed, 'l' to load account, or press Enter to go back: "
|
||||
)
|
||||
.strip()
|
||||
.lower()
|
||||
)
|
||||
if action == "r":
|
||||
seed = self.entry_manager.get_managed_account_seed(
|
||||
index, self.parent_seed
|
||||
)
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(seed, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] Seed phrase copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(color_text(seed, "deterministic"))
|
||||
# QR code display removed for managed account seed
|
||||
self._entry_actions_menu(index, entry)
|
||||
pause()
|
||||
return
|
||||
if action == "l":
|
||||
self.load_managed_account(index)
|
||||
return
|
||||
self._entry_actions_menu(index, entry)
|
||||
pause()
|
||||
return
|
||||
|
||||
website_name = entry.get("label", entry.get("website"))
|
||||
length = entry.get("length")
|
||||
username = entry.get("username")
|
||||
url = entry.get("url")
|
||||
blacklisted = entry.get("archived", entry.get("blacklisted"))
|
||||
notes = entry.get("notes", "")
|
||||
|
||||
print(
|
||||
colored(
|
||||
f"Retrieving password for '{website_name}' with length {length}.",
|
||||
"cyan",
|
||||
)
|
||||
)
|
||||
if username:
|
||||
print(colored(f"Username: {username}", "cyan"))
|
||||
if url:
|
||||
print(colored(f"URL: {url}", "cyan"))
|
||||
if blacklisted:
|
||||
self.notify(
|
||||
"Warning: This password is archived and should not be used.",
|
||||
level="WARNING",
|
||||
)
|
||||
|
||||
password = self.password_generator.generate_password(length, index)
|
||||
|
||||
if password:
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(password, self.clipboard_clear_delay)
|
||||
print(
|
||||
colored(
|
||||
f"[+] Password for '{website_name}' copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(
|
||||
colored(
|
||||
f"\n[+] Retrieved Password for {website_name}:\n",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
print(color_text(f"Password: {password}", "deterministic"))
|
||||
print(colored(f"Associated Username: {username or 'N/A'}", "cyan"))
|
||||
print(colored(f"Associated URL: {url or 'N/A'}", "cyan"))
|
||||
print(
|
||||
colored(
|
||||
f"Archived Status: {'Archived' if blacklisted else 'Active'}",
|
||||
"cyan",
|
||||
)
|
||||
)
|
||||
tags = entry.get("tags", [])
|
||||
if tags:
|
||||
print(colored(f"Tags: {', '.join(tags)}", "cyan"))
|
||||
custom_fields = entry.get("custom_fields", [])
|
||||
if custom_fields:
|
||||
print(colored("Additional Fields:", "cyan"))
|
||||
hidden_fields = []
|
||||
for field in custom_fields:
|
||||
label = field.get("label", "")
|
||||
value = field.get("value", "")
|
||||
if field.get("is_hidden"):
|
||||
hidden_fields.append((label, value))
|
||||
print(colored(f" {label}: [hidden]", "cyan"))
|
||||
else:
|
||||
print(colored(f" {label}: {value}", "cyan"))
|
||||
if hidden_fields:
|
||||
show = (
|
||||
input("Reveal hidden fields? (y/N): ").strip().lower()
|
||||
)
|
||||
if show == "y":
|
||||
for label, value in hidden_fields:
|
||||
if self.secret_mode_enabled:
|
||||
copy_to_clipboard(
|
||||
value, self.clipboard_clear_delay
|
||||
)
|
||||
print(
|
||||
colored(
|
||||
f"[+] {label} copied to clipboard. Will clear in {self.clipboard_clear_delay} seconds.",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(colored(f" {label}: {value}", "cyan"))
|
||||
else:
|
||||
print(colored("Error: Failed to retrieve the password.", "red"))
|
||||
self._entry_actions_menu(index, entry)
|
||||
pause()
|
||||
return
|
||||
except Exception as e:
|
||||
logging.error(f"Error during password retrieval: {e}", exc_info=True)
|
||||
print(colored(f"Error: Failed to retrieve password: {e}", "red"))
|
||||
@@ -3914,9 +3910,9 @@ class PasswordManager:
|
||||
)
|
||||
stats["backup_count"] = len(backups)
|
||||
stats["backup_dir"] = str(self.backup_manager.backup_dir)
|
||||
stats["additional_backup_path"] = (
|
||||
self.config_manager.get_additional_backup_path()
|
||||
)
|
||||
stats[
|
||||
"additional_backup_path"
|
||||
] = self.config_manager.get_additional_backup_path()
|
||||
|
||||
# Nostr sync info
|
||||
manifest = getattr(self.nostr_client, "current_manifest", None)
|
||||
|
Reference in New Issue
Block a user