#!/usr/bin/env python3 """ Password Generator for Emergency Access Server Generates secure passwords and their hashes for HTTP Basic Authentication """ import secrets import string import hashlib import argparse import json import getpass from typing import Dict, Any def generate_secure_password(length: int = 64) -> str: """ Generate a cryptographically secure password. Args: length: Password length (default: 16) Returns: Secure password string """ # Use a mix of letters, numbers, and safe symbols alphabet = string.ascii_letters + string.digits + "!@#$%^&*" return ''.join(secrets.choice(alphabet) for _ in range(length)) def generate_password_hash(password: str) -> str: """ Generate a password hash using PBKDF2 with SHA-256. Args: password: Plain text password Returns: Password hash in format "salt:hash" """ salt = secrets.token_hex(16) password_hash = hashlib.pbkdf2_hmac('sha256', password.encode(), salt.encode(), 100000) return f"{salt}:{password_hash.hex()}" def verify_password(password: str, password_hash: str) -> bool: """ Verify a password against its hash. Args: password: Plain text password password_hash: Hash in format "salt:hash" Returns: True if password matches, False otherwise """ try: salt, stored_hash = password_hash.split(':') computed_hash = hashlib.pbkdf2_hmac('sha256', password.encode(), salt.encode(), 100000) return computed_hash.hex() == stored_hash except Exception: return False def generate_key_credentials(key_id: str) -> Dict[str, str]: """ Generate username and password for a key. Args: key_id: Key identifier Returns: Dictionary with username, password, and password_hash """ username = f"emergency_{key_id}" password = generate_secure_password(64) # Longer for emergency keys password_hash = generate_password_hash(password) return { "username": username, "password": password, "password_hash": password_hash } def generate_health_credentials() -> Dict[str, str]: """ Generate username and password for health endpoint. Returns: Dictionary with username, password, and password_hash """ username = "health_monitor" password = generate_secure_password(16) password_hash = generate_password_hash(password) return { "username": username, "password": password, "password_hash": password_hash } def generate_full_credentials(keys: list) -> Dict[str, Any]: """ Generate all credentials for a configuration. Args: keys: List of key identifiers Returns: Dictionary with all credentials """ credentials = { "health": generate_health_credentials(), "keys": {} } for key_id in keys: credentials["keys"][key_id] = generate_key_credentials(key_id) return credentials def update_config_with_credentials(config_path: str, credentials: Dict[str, Any]) -> None: """ Update a configuration file with generated credentials. Args: config_path: Path to configuration file credentials: Generated credentials """ try: with open(config_path, 'r') as f: config = json.load(f) except FileNotFoundError: print(f"Configuration file {config_path} not found") return except json.JSONDecodeError: print(f"Invalid JSON in {config_path}") return # Update health credentials if "routes" not in config: config["routes"] = {} config["routes"]["health_username"] = credentials["health"]["username"] config["routes"]["health_password_hash"] = credentials["health"]["password_hash"] # Update key credentials if "keys" in config: for key_id, key_creds in credentials["keys"].items(): if key_id in config["keys"]: config["keys"][key_id]["username"] = key_creds["username"] config["keys"][key_id]["password_hash"] = key_creds["password_hash"] # Write updated config with open(config_path, 'w') as f: json.dump(config, f, indent=2) print(f"Updated configuration file: {config_path}") def print_credentials(credentials: Dict[str, Any], show_passwords: bool = True) -> None: """ Print generated credentials in a readable format. Args: credentials: Generated credentials show_passwords: Whether to show plain text passwords """ print("\n" + "=" * 60) print("GENERATED CREDENTIALS") print("=" * 60) print("\nHealth Endpoint:") print(f" Username: {credentials['health']['username']}") if show_passwords: print(f" Password: {credentials['health']['password']}") print(f" Hash: {credentials['health']['password_hash']}") print("\nKey Endpoints:") for key_id, key_creds in credentials["keys"].items(): print(f"\n {key_id}:") print(f" Username: {key_creds['username']}") if show_passwords: print(f" Password: {key_creds['password']}") print(f" Hash: {key_creds['password_hash']}") print("\n" + "=" * 60) if show_passwords: print("āš ļø SECURITY WARNING:") print(" Store these passwords securely!") print(" The plain text passwords are shown only once.") print(" Consider using a password manager.") print("\nšŸ’” Usage Examples:") print(f" curl -u {credentials['health']['username']}:PASSWORD http://localhost:1127/health-check") for key_id, key_creds in credentials["keys"].items(): print(f" curl -u {key_creds['username']}:PASSWORD http://localhost:1127/emergency-key-{key_id}") def interactive_password_setup() -> Dict[str, Any]: """ Interactive setup for custom passwords. Returns: Dictionary with credentials using user-provided passwords """ print("\n" + "=" * 60) print("INTERACTIVE PASSWORD SETUP") print("=" * 60) credentials = {"keys": {}} # Health endpoint password print("\nHealth Endpoint Setup:") while True: password = getpass.getpass("Enter password for health endpoint: ") confirm = getpass.getpass("Confirm password: ") if password == confirm and len(password) >= 8: credentials["health"] = { "username": "health_monitor", "password": password, "password_hash": generate_password_hash(password) } break elif password != confirm: print("āŒ Passwords don't match. Try again.") else: print("āŒ Password must be at least 8 characters. Try again.") # Key passwords print("\nKey Endpoint Setup:") key_count = int(input("How many keys do you want to configure? ")) for i in range(key_count): key_id = input(f"Enter key ID for key {i+1} (e.g., 'backup', 'master'): ").strip() while True: password = getpass.getpass(f"Enter password for {key_id}: ") confirm = getpass.getpass("Confirm password: ") if password == confirm and len(password) >= 12: credentials["keys"][key_id] = { "username": f"emergency_{key_id}", "password": password, "password_hash": generate_password_hash(password) } break elif password != confirm: print("āŒ Passwords don't match. Try again.") else: print("āŒ Key passwords must be at least 12 characters. Try again.") return credentials def main(): parser = argparse.ArgumentParser( description="Generate secure passwords and hashes for Emergency Access Server", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: # Generate credentials for specific keys python generate_passwords.py --keys backup master recovery # Generate and update config file python generate_passwords.py --keys backup master --update-config config.json # Interactive setup python generate_passwords.py --interactive # Hash a specific password python generate_passwords.py --hash-password "my_secure_password" # Verify a password against a hash python generate_passwords.py --verify-password "password" --hash "salt:hash" """ ) parser.add_argument( '--keys', '-k', nargs='+', help='Key identifiers to generate credentials for' ) parser.add_argument( '--update-config', '-u', metavar='CONFIG_FILE', help='Update configuration file with generated credentials' ) parser.add_argument( '--interactive', '-i', action='store_true', help='Interactive password setup' ) parser.add_argument( '--hash-password', metavar='PASSWORD', help='Generate hash for a specific password' ) parser.add_argument( '--verify-password', metavar='PASSWORD', help='Verify a password against a hash (use with --hash)' ) parser.add_argument( '--hash', metavar='HASH', help='Hash to verify password against' ) parser.add_argument( '--hide-passwords', action='store_true', help='Don\'t show plain text passwords in output' ) parser.add_argument( '--length', type=int, default=16, help='Password length for generated passwords (default: 16)' ) args = parser.parse_args() # Handle specific operations if args.hash_password: password_hash = generate_password_hash(args.hash_password) print(f"Password hash: {password_hash}") return if args.verify_password and args.hash: is_valid = verify_password(args.verify_password, args.hash) print(f"Password verification: {'āœ… VALID' if is_valid else 'āŒ INVALID'}") return # Generate credentials if args.interactive: credentials = interactive_password_setup() elif args.keys: credentials = generate_full_credentials(args.keys) else: # Default: generate for common keys credentials = generate_full_credentials(['backup', 'master']) # Print results print_credentials(credentials, show_passwords=not args.hide_passwords) # Update config if requested if args.update_config: update_config_with_credentials(args.update_config, credentials) # Save credentials to file credentials_file = "emergency_credentials.json" with open(credentials_file, 'w') as f: json.dump(credentials, f, indent=2) print(f"\nšŸ’¾ Credentials saved to: {credentials_file}") print("āš ļø Remember to store this file securely and delete it after copying passwords!") if __name__ == '__main__': main()