diff --git a/__pycache__/algorithms.cpython-312.pyc b/__pycache__/algorithms.cpython-312.pyc new file mode 100644 index 0000000..676f26f Binary files /dev/null and b/__pycache__/algorithms.cpython-312.pyc differ diff --git a/__pycache__/argon2_algorithm.cpython-312.pyc b/__pycache__/argon2_algorithm.cpython-312.pyc new file mode 100644 index 0000000..526b964 Binary files /dev/null and b/__pycache__/argon2_algorithm.cpython-312.pyc differ diff --git a/__pycache__/bcrypt_algorithm.cpython-312.pyc b/__pycache__/bcrypt_algorithm.cpython-312.pyc new file mode 100644 index 0000000..0e51414 Binary files /dev/null and b/__pycache__/bcrypt_algorithm.cpython-312.pyc differ diff --git a/__pycache__/pbkdf2_algorithm.cpython-312.pyc b/__pycache__/pbkdf2_algorithm.cpython-312.pyc new file mode 100644 index 0000000..7fd9af2 Binary files /dev/null and b/__pycache__/pbkdf2_algorithm.cpython-312.pyc differ diff --git a/__pycache__/salt.cpython-312.pyc b/__pycache__/salt.cpython-312.pyc index bde6280..8132b80 100644 Binary files a/__pycache__/salt.cpython-312.pyc and b/__pycache__/salt.cpython-312.pyc differ diff --git a/salt.py b/salt.py index 20a4d91..87071b9 100644 --- a/salt.py +++ b/salt.py @@ -46,18 +46,38 @@ def verify_password( def _build_parser() -> argparse.ArgumentParser: + from algorithms import list_algorithms + + available_algos = ", ".join(list_algorithms()) parser = argparse.ArgumentParser( - description="Erstellt sichere Password-Hashes (PBKDF2, Argon2, bcrypt) oder verifiziert bestehende Werte." + description=f"Erstellt sichere Password-Hashes oder verifiziert bestehende Werte. " + f"Verfügbare Algorithmen (--algorithm): {available_algos}" ) subparsers = parser.add_subparsers(dest="command") hash_parser = subparsers.add_parser( - "hash", help="Erstellt ein Salt und einen Hash für ein Passwort." + "hash", help=f"Erstellt ein Salt und einen Hash (--algorithm: {available_algos})." ) hash_parser.add_argument("password", help="Klartext-Passwort zum Hashen.") - verify_parser = subparsers.add_parser("verify", help="Überprüft ein Passwort.") + hash_parser.add_argument( + "--algorithm", + "-a", + choices=list_algorithms(), + default="pbkdf2", + help="Hash-Algorithmus (Standard: pbkdf2)", + ) + verify_parser = subparsers.add_parser( + "verify", help=f"Überprüft ein Passwort (--algorithm: {available_algos})." + ) verify_parser.add_argument("password", help="Klartext-Passwort zur Überprüfung.") verify_parser.add_argument("salt", help="Base64-kodiertes Salt.") verify_parser.add_argument("hash", help="Base64-kodierter Hash.") + verify_parser.add_argument( + "--algorithm", + "-a", + choices=list_algorithms(), + default="pbkdf2", + help="Hash-Algorithmus (Standard: pbkdf2)", + ) return parser @@ -65,8 +85,17 @@ def _normalize_args(argv: Sequence[str] | None) -> list[str]: """Erlaube ``python3 salt.py `` als Abkürzung für ``hash``.""" if not argv: return [] - if argv[0] in {"hash", "verify"} or argv[0].startswith("-"): + # If it starts with a subcommand, leave as-is + if argv[0] in {"hash", "verify"}: return list(argv) + # If it starts with help flags, leave as-is + if argv[0] in {"-h", "--help"}: + return list(argv) + # If it starts with --algorithm or -a, prepend "hash" + # This allows: python3 salt.py --algorithm argon2 mypassword + if argv[0] in {"--algorithm", "-a"}: + return ["hash", *argv] + # Otherwise (plain password), prepend "hash" return ["hash", *argv] @@ -76,7 +105,7 @@ def main(argv: Sequence[str] | None = None) -> int: args = parser.parse_args(_normalize_args(arg_list)) if args.command == "verify": - if verify_password(args.password, args.salt, args.hash): + if verify_password(args.password, args.salt, args.hash, algorithm=args.algorithm): print("✓ Passwort korrekt") return 0 print("✗ Passwort falsch") @@ -85,7 +114,7 @@ def main(argv: Sequence[str] | None = None) -> int: if args.command != "hash": parser.error('Bitte einen Hash generieren oder "verify" verwenden.') - salt, hash_value = hash_password(args.password) + salt, hash_value = hash_password(args.password, algorithm=args.algorithm) print(f"Salt: {salt}") print(f"Hash: {hash_value}") return 0 diff --git a/tests/__pycache__/test_algorithms.cpython-312-pytest-9.0.1.pyc b/tests/__pycache__/test_algorithms.cpython-312-pytest-9.0.1.pyc new file mode 100644 index 0000000..78dadd3 Binary files /dev/null and b/tests/__pycache__/test_algorithms.cpython-312-pytest-9.0.1.pyc differ diff --git a/tests/__pycache__/test_cli.cpython-312-pytest-9.0.1.pyc b/tests/__pycache__/test_cli.cpython-312-pytest-9.0.1.pyc index 35c11bc..22a0723 100644 Binary files a/tests/__pycache__/test_cli.cpython-312-pytest-9.0.1.pyc and b/tests/__pycache__/test_cli.cpython-312-pytest-9.0.1.pyc differ diff --git a/tests/__pycache__/test_hashing.cpython-312-pytest-9.0.1.pyc b/tests/__pycache__/test_hashing.cpython-312-pytest-9.0.1.pyc index 0b00562..3c68c5b 100644 Binary files a/tests/__pycache__/test_hashing.cpython-312-pytest-9.0.1.pyc and b/tests/__pycache__/test_hashing.cpython-312-pytest-9.0.1.pyc differ diff --git a/tests/test_cli.py b/tests/test_cli.py index f5808b6..ccd5007 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -9,3 +9,33 @@ def test_main_verify_round_trip() -> None: salt, hashed = hash_password("secret-value") assert main(["verify", "secret-value", salt, hashed]) == 0 assert main(["verify", "wrong", salt, hashed]) == 1 + + +def test_main_hash_with_algorithm_pbkdf2() -> None: + """Test hash command with --algorithm pbkdf2.""" + assert main(["hash", "--algorithm", "pbkdf2", "test-password"]) == 0 + + +def test_main_hash_with_algorithm_argon2() -> None: + """Test hash command with --algorithm argon2.""" + assert main(["hash", "--algorithm", "argon2", "test-password"]) == 0 + + +def test_main_hash_with_algorithm_bcrypt() -> None: + """Test hash command with --algorithm bcrypt.""" + assert main(["hash", "--algorithm", "bcrypt", "test-password"]) == 0 + + +def test_main_verify_with_algorithm() -> None: + """Test verify command with --algorithm option.""" + # Test with each algorithm + for algo in ["pbkdf2", "argon2", "bcrypt"]: + salt, hashed = hash_password("test-pw", algorithm=algo) + assert main(["verify", "--algorithm", algo, "test-pw", salt, hashed]) == 0 + assert main(["verify", "--algorithm", algo, "wrong", salt, hashed]) == 1 + + +def test_main_hash_algorithm_shortcut() -> None: + """Test shortcut syntax with --algorithm option.""" + # Should support: python3 salt.py --algorithm argon2 mypassword + assert main(["--algorithm", "argon2", "test-password"]) == 0