| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- #!/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()
|