From 5c4371f787f901768ff2e6fd79d194bed4472d5e Mon Sep 17 00:00:00 2001 From: Joachim Hummel Date: Thu, 13 Nov 2025 13:11:07 +0000 Subject: [PATCH] feat: add algorithm interface and registry --- algorithms.py | 40 ++++++++++++++++++++++++++++++++++++++++ tests/test_algorithms.py | 23 +++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 algorithms.py create mode 100644 tests/test_algorithms.py diff --git a/algorithms.py b/algorithms.py new file mode 100644 index 0000000..c549f16 --- /dev/null +++ b/algorithms.py @@ -0,0 +1,40 @@ +"""Algorithm interface and registry for password hashing.""" + +from __future__ import annotations + +from typing import Protocol + + +class Algorithm(Protocol): + """Protocol defining the interface for password hashing algorithms.""" + + identifier: str + + def hash(self, password: str, **kwargs) -> tuple[str, str]: + """Hash a password and return (salt_b64, hash_b64).""" + ... + + def verify(self, password: str, salt_b64: str, hash_b64: str, **kwargs) -> bool: + """Verify a password against stored salt and hash.""" + ... + + +# Algorithm registry +_ALGORITHMS: dict[str, Algorithm] = {} + + +def register_algorithm(algo: Algorithm) -> None: + """Register an algorithm in the global registry.""" + _ALGORITHMS[algo.identifier] = algo + + +def get_algorithm(name: str) -> Algorithm: + """Get an algorithm by name from the registry.""" + if name not in _ALGORITHMS: + raise ValueError(f"Unknown algorithm: {name}") + return _ALGORITHMS[name] + + +def list_algorithms() -> list[str]: + """Return list of registered algorithm identifiers.""" + return sorted(_ALGORITHMS.keys()) diff --git a/tests/test_algorithms.py b/tests/test_algorithms.py new file mode 100644 index 0000000..ac4a15e --- /dev/null +++ b/tests/test_algorithms.py @@ -0,0 +1,23 @@ +import pytest + +from algorithms import Algorithm, get_algorithm + + +def test_algorithm_has_required_methods(): + """Verify Algorithm protocol defines required methods.""" + algo = get_algorithm("pbkdf2") + assert hasattr(algo, "hash") + assert hasattr(algo, "verify") + assert hasattr(algo, "identifier") + + +def test_get_algorithm_returns_pbkdf2(): + """Verify default algorithm is PBKDF2.""" + algo = get_algorithm("pbkdf2") + assert algo.identifier == "pbkdf2" + + +def test_get_algorithm_unknown_raises_error(): + """Verify unknown algorithm raises ValueError.""" + with pytest.raises(ValueError, match="Unknown algorithm"): + get_algorithm("unknown")