Implementation Approaches
Implementation Approaches
Several peppering strategies exist, each with distinct security properties and operational characteristics. Pre-hashing peppering applies the pepper before the main password hashing algorithm, while post-hashing applies it after. HMAC-based peppering uses the pepper as a key for message authentication. Each approach offers different trade-offs between security, performance, and compatibility.
import time
import base64
from enum import Enum
class PepperStrategy(Enum):
PREPEND = "prepend"
HMAC_FIRST = "hmac_first"
ENCRYPT_HASH = "encrypt_hash"
DOUBLE_HASH = "double_hash"
class AdvancedPepperSystem:
"""Comprehensive pepper implementation with multiple strategies"""
def __init__(self, pepper_key=None):
self.pepper = pepper_key or secrets.token_bytes(32)
self.ph = PasswordHasher(time_cost=3, memory_cost=65536)
def apply_pepper(self, password, strategy=PepperStrategy.HMAC_FIRST):
"""Apply pepper using specified strategy"""
if strategy == PepperStrategy.PREPEND:
# Simple prepending (least secure)
return self.pepper.hex() + password
elif strategy == PepperStrategy.HMAC_FIRST:
# HMAC before hashing (recommended)
peppered = hmac.new(
self.pepper,
password.encode('utf-8'),
hashlib.sha256
).digest()
return base64.b64encode(peppered).decode('ascii')
elif strategy == PepperStrategy.DOUBLE_HASH:
# Hash with pepper as salt component
intermediate = hashlib.pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
self.pepper,
100000
)
return base64.b64encode(intermediate).decode('ascii')
def hash_password(self, password, strategy=PepperStrategy.HMAC_FIRST):
"""Hash password with selected pepper strategy"""
# Apply pepper
peppered = self.apply_pepper(password, strategy)
# Hash with Argon2
hash_value = self.ph.hash(peppered)
# Store strategy for verification
return f"{strategy.value}${hash_value}"
def verify_password(self, password, stored_hash):
"""Verify password with appropriate pepper strategy"""
try:
# Extract strategy and hash
strategy_str, hash_value = stored_hash.split('$', 1)
strategy = PepperStrategy(strategy_str)
# Apply same pepper strategy
peppered = self.apply_pepper(password, strategy)
# Verify with Argon2
return self.ph.verify(hash_value, peppered)
except Exception:
return False
def benchmark_strategies(self):
"""Compare performance of different strategies"""
password = "BenchmarkPassword123!"
iterations = 100
print("\nPepper Strategy Performance Comparison:")
print("-" * 50)
for strategy in PepperStrategy:
start = time.time()
for _ in range(iterations):
self.hash_password(password, strategy)
elapsed = time.time() - start
per_hash = elapsed / iterations
print(f"{strategy.value:12}: {per_hash*1000:.1f}ms per hash")
# Demonstrate different strategies
pepper_system = AdvancedPepperSystem()
# Test each strategy
password = "TestPassword123!"
print("Different Pepper Strategies:\n")
for strategy in PepperStrategy:
hashed = pepper_system.hash_password(password, strategy)
print(f"{strategy.value}:")
print(f" Hash: {hashed[:50]}...")
# Verify
assert pepper_system.verify_password(password, hashed)
assert not pepper_system.verify_password("wrong", hashed)
print(f" ✓ Verification works\n")
# Benchmark performance
pepper_system.benchmark_strategies()
HMAC-based peppering provides the strongest security properties by cryptographically binding the password and pepper. This approach ensures that any modification to either value produces completely different results, preventing length extension attacks and providing standard cryptographic guarantees. The HMAC output can be used directly as input to password hashing functions, maintaining compatibility with existing systems.