How to mock Argon2 password hashing in Python
This code demonstrates a mock Argon2 password hasher using HMAC-SHA256 iterations, providing hash and verify methods that mimic Argon2's salted, iterated derivation.
Python code
43 linesimport hashlib
import hmac
import os
class Argon2Mock:
def __init__(self, salt_size=16, hash_len=32):
self.salt_size = salt_size
self.hash_len = hash_len
def hash(self, password: str, salt: bytes = None) -> str:
if salt is None:
salt = os.urandom(self.salt_size)
elif len(salt) != self.salt_size:
raise ValueError(f"Salt must be {self.salt_size} bytes")
# Mock Argon2 derivation: iterate HMAC-SHA256 to simulate hashing
derived = salt
for _ in range(10000): # Simple iteration count
derived = hmac.new(derived, password.encode(), hashlib.sha256).digest()
derived = derived[:self.hash_len]
return f"argon2$v=19$m=65536,t=3,p=4$" \
f"{salt.hex()}$" \
f"{derived.hex()}"
def verify(self, password: str, encoded: str) -> bool:
try:
parts = encoded.split("$")
salt_hex, hash_hex = parts[-2], parts[-1]
salt = bytes.fromhex(salt_hex)
expected = self.hash(password, salt)
return hmac.compare_digest(expected, encoded)
except Exception:
return False
if __name__ == "__main__":
argon = Argon2Mock()
hashed = argon.hash("mySecretPass!42")
print("Encoded:", hashed)
print("Valid password:", argon.verify("mySecretPass!42", hashed))
print("Invalid password:", argon.verify("wrongPass", hashed))
Output
Encoded: argon2$v=19$m=65536,t=3,p=4$<salt_hex>$<hash_hex>
Valid password: True
Invalid password: False
How it works
This mock simulates Argon2's core idea: a salt, iterative hashing, and a formatted output string. The HMAC-SHA256 loop repeats 10,000 times to emulate the CPU cost. The salt prevents rainbow table attacks, and the output format includes parameters for potential verification. Real Argon2 uses specialized algorithms (Argon2d, Argon2i, Argon2id) tuned for memory hardness; this mock is for education or prototyping only. The verify method extracts the salt and re-derives the hash, then compares with hmac.compare_digest to prevent timing attacks.
Common mistakes
- Using a fixed salt, which defeats the purpose of salting
- Forgetting to check the salt length before hashing
- Comparing hashes with plain equality instead of hmac.compare_digest
- Treating this mock as secure for production
Variations
- Use the 'argon2-cffi' library (pip install argon2-cffi) for real Argon2 hashing
- Implement a different iteration count or use 'hashlib.pbkdf2_hmac' for a more standard KDF
Real-world use cases
- Prototyping a user registration flow before integrating with a real password hashing library.
- Writing unit tests for authentication logic without requiring external Argon2 dependencies.
- Teaching password hashing concepts in a controlled environment where security is not the goal.
Sponsored
More from Auth & security at scale
- ACME LetsEncrypt Mock Challenge Server in Python medium
- AES GCM encryption and decryption in Python medium
- Build a Mock OIDC Userinfo Endpoint in Python with Flask easy
- ChaCha20-Poly1305 mock in Python medium
- ECDH key agreement in Python with cryptography medium
- Enforce TLS 1.2 Minimum in Python easy
Keep learning
Related tutorials and quizzes for this topic.