mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-10 00:09:04 +00:00
Format key_manager
This commit is contained in:
@@ -8,17 +8,17 @@ try:
|
||||
from .key_derivation import derive_key_from_password, derive_key_from_parent_seed
|
||||
from .checksum import calculate_checksum, verify_checksum
|
||||
from .password_prompt import prompt_for_password
|
||||
|
||||
|
||||
logging.info("Modules imported successfully.")
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to import one or more modules: {e}")
|
||||
logging.error(traceback.format_exc()) # Log full traceback
|
||||
|
||||
__all__ = [
|
||||
'derive_key_from_password',
|
||||
'derive_key_from_parent_seed',
|
||||
'calculate_checksum',
|
||||
'verify_checksum',
|
||||
'lock_file',
|
||||
'prompt_for_password'
|
||||
"derive_key_from_password",
|
||||
"derive_key_from_parent_seed",
|
||||
"calculate_checksum",
|
||||
"verify_checksum",
|
||||
"lock_file",
|
||||
"prompt_for_password",
|
||||
]
|
||||
|
@@ -19,14 +19,12 @@ from typing import Optional
|
||||
|
||||
from termcolor import colored
|
||||
|
||||
from constants import (
|
||||
APP_DIR,
|
||||
SCRIPT_CHECKSUM_FILE
|
||||
)
|
||||
from constants import APP_DIR, SCRIPT_CHECKSUM_FILE
|
||||
|
||||
# Instantiate the logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def calculate_checksum(file_path: str) -> Optional[str]:
|
||||
"""
|
||||
Calculates the SHA-256 checksum of the given file.
|
||||
@@ -39,7 +37,7 @@ def calculate_checksum(file_path: str) -> Optional[str]:
|
||||
"""
|
||||
hasher = hashlib.sha256()
|
||||
try:
|
||||
with open(file_path, 'rb') as f:
|
||||
with open(file_path, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(4096), b""):
|
||||
hasher.update(chunk)
|
||||
checksum = hasher.hexdigest()
|
||||
@@ -47,12 +45,20 @@ def calculate_checksum(file_path: str) -> Optional[str]:
|
||||
return checksum
|
||||
except FileNotFoundError:
|
||||
logging.error(f"File '{file_path}' not found for checksum calculation.")
|
||||
print(colored(f"Error: File '{file_path}' not found for checksum calculation.", 'red'))
|
||||
print(
|
||||
colored(
|
||||
f"Error: File '{file_path}' not found for checksum calculation.", "red"
|
||||
)
|
||||
)
|
||||
return None
|
||||
except Exception as e:
|
||||
logging.error(f"Error calculating checksum for '{file_path}': {e}")
|
||||
logging.error(traceback.format_exc()) # Log full traceback
|
||||
print(colored(f"Error: Failed to calculate checksum for '{file_path}': {e}", 'red'))
|
||||
print(
|
||||
colored(
|
||||
f"Error: Failed to calculate checksum for '{file_path}': {e}", "red"
|
||||
)
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
@@ -68,7 +74,7 @@ def verify_checksum(current_checksum: str, checksum_file_path: str) -> bool:
|
||||
bool: True if checksums match, False otherwise.
|
||||
"""
|
||||
try:
|
||||
with open(checksum_file_path, 'r') as f:
|
||||
with open(checksum_file_path, "r") as f:
|
||||
stored_checksum = f.read().strip()
|
||||
if current_checksum == stored_checksum:
|
||||
logging.debug(f"Checksum verification passed for '{checksum_file_path}'.")
|
||||
@@ -78,12 +84,17 @@ def verify_checksum(current_checksum: str, checksum_file_path: str) -> bool:
|
||||
return False
|
||||
except FileNotFoundError:
|
||||
logging.error(f"Checksum file '{checksum_file_path}' not found.")
|
||||
print(colored(f"Error: Checksum file '{checksum_file_path}' not found.", 'red'))
|
||||
print(colored(f"Error: Checksum file '{checksum_file_path}' not found.", "red"))
|
||||
return False
|
||||
except Exception as e:
|
||||
logging.error(f"Error reading checksum file '{checksum_file_path}': {e}")
|
||||
logging.error(traceback.format_exc()) # Log full traceback
|
||||
print(colored(f"Error: Failed to read checksum file '{checksum_file_path}': {e}", 'red'))
|
||||
print(
|
||||
colored(
|
||||
f"Error: Failed to read checksum file '{checksum_file_path}': {e}",
|
||||
"red",
|
||||
)
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
@@ -100,16 +111,21 @@ def update_checksum(content: str, checksum_file_path: str) -> bool:
|
||||
"""
|
||||
try:
|
||||
hasher = hashlib.sha256()
|
||||
hasher.update(content.encode('utf-8'))
|
||||
hasher.update(content.encode("utf-8"))
|
||||
new_checksum = hasher.hexdigest()
|
||||
with open(checksum_file_path, 'w') as f:
|
||||
with open(checksum_file_path, "w") as f:
|
||||
f.write(new_checksum)
|
||||
logging.debug(f"Updated checksum for '{checksum_file_path}' to: {new_checksum}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to update checksum for '{checksum_file_path}': {e}")
|
||||
logging.error(traceback.format_exc()) # Log full traceback
|
||||
print(colored(f"Error: Failed to update checksum for '{checksum_file_path}': {e}", 'red'))
|
||||
print(
|
||||
colored(
|
||||
f"Error: Failed to update checksum for '{checksum_file_path}': {e}",
|
||||
"red",
|
||||
)
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
@@ -129,11 +145,11 @@ def verify_and_update_checksum(file_path: str, checksum_file_path: str) -> bool:
|
||||
return False
|
||||
|
||||
if verify_checksum(current_checksum, checksum_file_path):
|
||||
print(colored(f"Checksum verification passed for '{file_path}'.", 'green'))
|
||||
print(colored(f"Checksum verification passed for '{file_path}'.", "green"))
|
||||
logging.info(f"Checksum verification passed for '{file_path}'.")
|
||||
return True
|
||||
else:
|
||||
print(colored(f"Checksum verification failed for '{file_path}'.", 'red'))
|
||||
print(colored(f"Checksum verification failed for '{file_path}'.", "red"))
|
||||
logging.warning(f"Checksum verification failed for '{file_path}'.")
|
||||
return False
|
||||
|
||||
@@ -154,13 +170,20 @@ def initialize_checksum(file_path: str, checksum_file_path: str) -> bool:
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(checksum_file_path, 'w') as f:
|
||||
with open(checksum_file_path, "w") as f:
|
||||
f.write(checksum)
|
||||
logging.debug(f"Initialized checksum file '{checksum_file_path}' with checksum: {checksum}")
|
||||
print(colored(f"Initialized checksum for '{file_path}'.", 'green'))
|
||||
logging.debug(
|
||||
f"Initialized checksum file '{checksum_file_path}' with checksum: {checksum}"
|
||||
)
|
||||
print(colored(f"Initialized checksum for '{file_path}'.", "green"))
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to initialize checksum file '{checksum_file_path}': {e}")
|
||||
logging.error(traceback.format_exc()) # Log full traceback
|
||||
print(colored(f"Error: Failed to initialize checksum file '{checksum_file_path}': {e}", 'red'))
|
||||
print(
|
||||
colored(
|
||||
f"Error: Failed to initialize checksum file '{checksum_file_path}': {e}",
|
||||
"red",
|
||||
)
|
||||
)
|
||||
return False
|
||||
|
@@ -26,6 +26,7 @@ import traceback
|
||||
# Instantiate the logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def lock_file(file_path: Path, lock_type: int) -> Generator[None, None, None]:
|
||||
"""
|
||||
@@ -44,14 +45,16 @@ def lock_file(file_path: Path, lock_type: int) -> Generator[None, None, None]:
|
||||
SystemExit: Exits the program if the lock cannot be acquired.
|
||||
"""
|
||||
if lock_type not in (fcntl.LOCK_EX, fcntl.LOCK_SH):
|
||||
logging.error(f"Invalid lock type: {lock_type}. Use fcntl.LOCK_EX or fcntl.LOCK_SH.")
|
||||
print(colored("Error: Invalid lock type provided.", 'red'))
|
||||
logging.error(
|
||||
f"Invalid lock type: {lock_type}. Use fcntl.LOCK_EX or fcntl.LOCK_SH."
|
||||
)
|
||||
print(colored("Error: Invalid lock type provided.", "red"))
|
||||
sys.exit(1)
|
||||
|
||||
file = None
|
||||
try:
|
||||
# Determine the mode based on whether the file exists
|
||||
mode = 'rb+' if file_path.exists() else 'wb'
|
||||
mode = "rb+" if file_path.exists() else "wb"
|
||||
|
||||
# Open the file
|
||||
file = open(file_path, mode)
|
||||
@@ -67,7 +70,12 @@ def lock_file(file_path: Path, lock_type: int) -> Generator[None, None, None]:
|
||||
lock_type_str = "exclusive" if lock_type == fcntl.LOCK_EX else "shared"
|
||||
logging.error(f"Failed to acquire {lock_type_str} lock on '{file_path}': {e}")
|
||||
logging.error(traceback.format_exc()) # Log full traceback
|
||||
print(colored(f"Error: Failed to acquire {lock_type_str} lock on '{file_path}': {e}", 'red'))
|
||||
print(
|
||||
colored(
|
||||
f"Error: Failed to acquire {lock_type_str} lock on '{file_path}': {e}",
|
||||
"red",
|
||||
)
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
finally:
|
||||
@@ -78,9 +86,16 @@ def lock_file(file_path: Path, lock_type: int) -> Generator[None, None, None]:
|
||||
logging.debug(f"Lock released on '{file_path}'.")
|
||||
except Exception as e:
|
||||
lock_type_str = "exclusive" if lock_type == fcntl.LOCK_EX else "shared"
|
||||
logging.warning(f"Failed to release {lock_type_str} lock on '{file_path}': {e}")
|
||||
logging.warning(
|
||||
f"Failed to release {lock_type_str} lock on '{file_path}': {e}"
|
||||
)
|
||||
logging.error(traceback.format_exc()) # Log full traceback
|
||||
print(colored(f"Warning: Failed to release {lock_type_str} lock on '{file_path}': {e}", 'yellow'))
|
||||
print(
|
||||
colored(
|
||||
f"Warning: Failed to release {lock_type_str} lock on '{file_path}': {e}",
|
||||
"yellow",
|
||||
)
|
||||
)
|
||||
finally:
|
||||
# Close the file
|
||||
try:
|
||||
@@ -89,7 +104,12 @@ def lock_file(file_path: Path, lock_type: int) -> Generator[None, None, None]:
|
||||
except Exception as e:
|
||||
logging.warning(f"Failed to close file '{file_path}': {e}")
|
||||
logging.error(traceback.format_exc()) # Log full traceback
|
||||
print(colored(f"Warning: Failed to close file '{file_path}': {e}", 'yellow'))
|
||||
print(
|
||||
colored(
|
||||
f"Warning: Failed to close file '{file_path}': {e}",
|
||||
"yellow",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@contextmanager
|
||||
|
@@ -16,6 +16,7 @@ from typing import Optional
|
||||
# Instantiate the logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def generate_fingerprint(seed_phrase: str, length: int = 16) -> Optional[str]:
|
||||
"""
|
||||
Generates a unique fingerprint from the provided seed phrase using SHA-256.
|
||||
@@ -33,7 +34,7 @@ def generate_fingerprint(seed_phrase: str, length: int = 16) -> Optional[str]:
|
||||
logger.debug(f"Normalized seed: {normalized_seed}")
|
||||
|
||||
# Compute SHA-256 hash
|
||||
sha256_hash = hashlib.sha256(normalized_seed.encode('utf-8')).hexdigest()
|
||||
sha256_hash = hashlib.sha256(normalized_seed.encode("utf-8")).hexdigest()
|
||||
logger.debug(f"SHA-256 Hash: {sha256_hash}")
|
||||
|
||||
# Truncate to desired length
|
||||
|
@@ -14,6 +14,7 @@ from utils.fingerprint import generate_fingerprint
|
||||
# Instantiate the logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FingerprintManager:
|
||||
"""
|
||||
FingerprintManager Class
|
||||
@@ -31,7 +32,7 @@ class FingerprintManager:
|
||||
app_dir (Path): The root application directory (e.g., ~/.seedpass).
|
||||
"""
|
||||
self.app_dir = app_dir
|
||||
self.fingerprints_file = self.app_dir / 'fingerprints.json'
|
||||
self.fingerprints_file = self.app_dir / "fingerprints.json"
|
||||
self._ensure_app_directory()
|
||||
self.fingerprints = self._load_fingerprints()
|
||||
self.current_fingerprint: Optional[str] = None
|
||||
@@ -43,7 +44,7 @@ class FingerprintManager:
|
||||
Returns:
|
||||
Optional[Path]: The Path object of the current fingerprint directory or None.
|
||||
"""
|
||||
if hasattr(self, 'current_fingerprint') and self.current_fingerprint:
|
||||
if hasattr(self, "current_fingerprint") and self.current_fingerprint:
|
||||
return self.get_fingerprint_directory(self.current_fingerprint)
|
||||
else:
|
||||
logger.error("No current fingerprint is set.")
|
||||
@@ -57,7 +58,9 @@ class FingerprintManager:
|
||||
self.app_dir.mkdir(parents=True, exist_ok=True)
|
||||
logger.debug(f"Application directory ensured at {self.app_dir}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create application directory at {self.app_dir}: {e}")
|
||||
logger.error(
|
||||
f"Failed to create application directory at {self.app_dir}: {e}"
|
||||
)
|
||||
logger.error(traceback.format_exc())
|
||||
raise
|
||||
|
||||
@@ -70,13 +73,15 @@ class FingerprintManager:
|
||||
"""
|
||||
try:
|
||||
if self.fingerprints_file.exists():
|
||||
with open(self.fingerprints_file, 'r') as f:
|
||||
with open(self.fingerprints_file, "r") as f:
|
||||
data = json.load(f)
|
||||
fingerprints = data.get('fingerprints', [])
|
||||
fingerprints = data.get("fingerprints", [])
|
||||
logger.debug(f"Loaded fingerprints: {fingerprints}")
|
||||
return fingerprints
|
||||
else:
|
||||
logger.debug("fingerprints.json not found. Initializing empty fingerprint list.")
|
||||
logger.debug(
|
||||
"fingerprints.json not found. Initializing empty fingerprint list."
|
||||
)
|
||||
return []
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load fingerprints: {e}")
|
||||
@@ -88,8 +93,8 @@ class FingerprintManager:
|
||||
Saves the current list of fingerprints to the fingerprints.json file.
|
||||
"""
|
||||
try:
|
||||
with open(self.fingerprints_file, 'w') as f:
|
||||
json.dump({'fingerprints': self.fingerprints}, f, indent=4)
|
||||
with open(self.fingerprints_file, "w") as f:
|
||||
json.dump({"fingerprints": self.fingerprints}, f, indent=4)
|
||||
logger.debug(f"Fingerprints saved: {self.fingerprints}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save fingerprints: {e}")
|
||||
@@ -140,7 +145,7 @@ class FingerprintManager:
|
||||
# Remove fingerprint directory
|
||||
fingerprint_dir = self.app_dir / fingerprint
|
||||
if fingerprint_dir.exists() and fingerprint_dir.is_dir():
|
||||
for child in fingerprint_dir.glob('*'):
|
||||
for child in fingerprint_dir.glob("*"):
|
||||
if child.is_file():
|
||||
child.unlink()
|
||||
elif child.is_dir():
|
||||
|
@@ -29,6 +29,7 @@ colorama_init()
|
||||
# Instantiate the logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def prompt_new_password() -> str:
|
||||
"""
|
||||
Prompts the user to enter and confirm a new password for encrypting the parent seed.
|
||||
@@ -51,39 +52,50 @@ def prompt_new_password() -> str:
|
||||
confirm_password = getpass.getpass(prompt="Confirm your password: ").strip()
|
||||
|
||||
if not password:
|
||||
print(colored("Error: Password cannot be empty. Please try again.", 'red'))
|
||||
print(
|
||||
colored("Error: Password cannot be empty. Please try again.", "red")
|
||||
)
|
||||
logging.warning("User attempted to enter an empty password.")
|
||||
attempts += 1
|
||||
continue
|
||||
|
||||
if len(password) < MIN_PASSWORD_LENGTH:
|
||||
print(colored(f"Error: Password must be at least {MIN_PASSWORD_LENGTH} characters long.", 'red'))
|
||||
logging.warning(f"User entered a password shorter than {MIN_PASSWORD_LENGTH} characters.")
|
||||
print(
|
||||
colored(
|
||||
f"Error: Password must be at least {MIN_PASSWORD_LENGTH} characters long.",
|
||||
"red",
|
||||
)
|
||||
)
|
||||
logging.warning(
|
||||
f"User entered a password shorter than {MIN_PASSWORD_LENGTH} characters."
|
||||
)
|
||||
attempts += 1
|
||||
continue
|
||||
|
||||
if password != confirm_password:
|
||||
print(colored("Error: Passwords do not match. Please try again.", 'red'))
|
||||
print(
|
||||
colored("Error: Passwords do not match. Please try again.", "red")
|
||||
)
|
||||
logging.warning("User entered mismatching passwords.")
|
||||
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 a valid and confirmed password.")
|
||||
return normalized_password
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(colored("\nOperation cancelled by user.", 'yellow'))
|
||||
print(colored("\nOperation cancelled by user.", "yellow"))
|
||||
logging.info("Password prompt interrupted by user.")
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
logging.error(f"Unexpected error during password prompt: {e}")
|
||||
logging.error(traceback.format_exc()) # Log full traceback
|
||||
print(colored(f"Error: {e}", 'red'))
|
||||
print(colored(f"Error: {e}", "red"))
|
||||
attempts += 1
|
||||
|
||||
print(colored("Maximum password attempts exceeded. Exiting.", 'red'))
|
||||
print(colored("Maximum password attempts exceeded. Exiting.", "red"))
|
||||
logging.error("User failed to provide a valid password after multiple attempts.")
|
||||
sys.exit(1)
|
||||
|
||||
@@ -107,27 +119,29 @@ def prompt_existing_password(prompt_message: str = "Enter your password: ") -> s
|
||||
password = getpass.getpass(prompt=prompt_message).strip()
|
||||
|
||||
if not password:
|
||||
print(colored("Error: Password cannot be empty.", 'red'))
|
||||
print(colored("Error: Password cannot be empty.", "red"))
|
||||
logging.warning("User attempted to enter an empty password.")
|
||||
sys.exit(1)
|
||||
|
||||
# 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.")
|
||||
return normalized_password
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(colored("\nOperation cancelled by user.", 'yellow'))
|
||||
print(colored("\nOperation cancelled by user.", "yellow"))
|
||||
logging.info("Existing password prompt interrupted by user.")
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
logging.error(f"Unexpected error during existing password prompt: {e}")
|
||||
logging.error(traceback.format_exc()) # Log full traceback
|
||||
print(colored(f"Error: {e}", 'red'))
|
||||
print(colored(f"Error: {e}", "red"))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def confirm_action(prompt_message: str = "Are you sure you want to proceed? (Y/N): ") -> bool:
|
||||
def confirm_action(
|
||||
prompt_message: str = "Are you sure you want to proceed? (Y/N): ",
|
||||
) -> bool:
|
||||
"""
|
||||
Prompts the user to confirm an action, typically used before performing critical operations.
|
||||
|
||||
@@ -143,24 +157,24 @@ def confirm_action(prompt_message: str = "Are you sure you want to proceed? (Y/N
|
||||
"""
|
||||
try:
|
||||
while True:
|
||||
response = input(colored(prompt_message, 'cyan')).strip().lower()
|
||||
if response in ['y', 'yes']:
|
||||
response = input(colored(prompt_message, "cyan")).strip().lower()
|
||||
if response in ["y", "yes"]:
|
||||
logging.debug("User confirmed the action.")
|
||||
return True
|
||||
elif response in ['n', 'no']:
|
||||
elif response in ["n", "no"]:
|
||||
logging.debug("User declined the action.")
|
||||
return False
|
||||
else:
|
||||
print(colored("Please respond with 'Y' or 'N'.", 'yellow'))
|
||||
print(colored("Please respond with 'Y' or 'N'.", "yellow"))
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(colored("\nOperation cancelled by user.", 'yellow'))
|
||||
print(colored("\nOperation cancelled by user.", "yellow"))
|
||||
logging.info("Action confirmation interrupted by user.")
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
logging.error(f"Unexpected error during action confirmation: {e}")
|
||||
logging.error(traceback.format_exc()) # Log full traceback
|
||||
print(colored(f"Error: {e}", 'red'))
|
||||
print(colored(f"Error: {e}", "red"))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user