"""Functions related to handling the password database master password.""" import secrets from pathlib import Path from sshecret.crypto import ( create_private_rsa_key, load_private_key, encrypt_string, decode_string, ) from sshecret_admin.core.settings import AdminServerSettings KEY_FILENAME = "sshecret-admin-key" def setup_master_password( settings: AdminServerSettings, filename: str = KEY_FILENAME, regenerate: bool = False, ) -> str | None: """Setup master password. If regenerate is True, a new key will be generated. This method should run just after setting up the database. """ keyfile = Path(filename) if settings.password_manager_directory: keyfile = settings.password_manager_directory / filename created = _initial_key_setup(settings, keyfile, regenerate) if not created: return None return _generate_master_password(settings, keyfile) def decrypt_master_password( settings: AdminServerSettings, encrypted: str, filename: str = KEY_FILENAME ) -> str: """Retrieve master password.""" keyfile = Path(filename) if settings.password_manager_directory: keyfile = settings.password_manager_directory / filename if not keyfile.exists(): raise RuntimeError("Error: Private key has not been generated yet.") private_key = load_private_key(str(keyfile.absolute()), password=settings.secret_key) return decode_string(encrypted, private_key) def _generate_password() -> str: """Generate a password.""" return secrets.token_urlsafe(32) def _initial_key_setup( settings: AdminServerSettings, keyfile: Path, regenerate: bool = False, ) -> bool: """Set up initial keys.""" if keyfile.exists() and not regenerate: return False assert settings.secret_key is not None, ( "Error: Could not load a secret key from environment." ) create_private_rsa_key(keyfile, password=settings.secret_key) return True def _generate_master_password( settings: AdminServerSettings, keyfile: Path ) -> str: """Generate master password for password database. Returns the encrypted string, base64 encoded. """ if not keyfile.exists(): raise RuntimeError("Error: Private key has not been generated yet.") private_key = load_private_key(str(keyfile.absolute()), password=settings.secret_key) public_key = private_key.public_key() master_password = _generate_password() return encrypt_string(master_password, public_key)