Argon2: The Modern Standard

Argon2: The Modern Standard

Argon2, winner of the 2015 Password Hashing Competition, represents the current state-of-the-art in password hashing. Designed by Alex Biryukov, Daniel Dinu, and Dmitry Khovratovich, Argon2 addresses limitations in earlier algorithms while providing superior resistance to various attack vectors. The algorithm comes in three variants: Argon2i (data-independent), Argon2d (data-dependent), and Argon2id (hybrid).

Argon2's design provides extensive parameterization: memory size, iteration count, and parallelism degree. This flexibility allows precise tuning for different security requirements and hardware constraints. The algorithm fills memory with pseudorandom data in multiple passes, with later passes depending on earlier ones, preventing attackers from computing just the final result. The memory access patterns resist both time-memory trade-off attacks and side-channel attacks.

import argon2
import time
from argon2 import PasswordHasher, Type
from argon2.low_level import hash_secret_raw

def demonstrate_argon2():
    """Demonstrate Argon2 variants and parameters"""
    
    password = b"SecurePassword123!"
    salt = b"somesaltvalue123"
    
    # Compare Argon2 variants
    print("Argon2 Variants Comparison:\n")
    
    variants = [
        (Type.I, "Argon2i  (side-channel resistant)"),
        (Type.D, "Argon2d  (faster, GPU resistant)"),
        (Type.ID, "Argon2id (hybrid, recommended)")
    ]
    
    for variant, desc in variants:
        start = time.time()
        hash_result = hash_secret_raw(
            secret=password,
            salt=salt,
            time_cost=3,
            memory_cost=65536,
            parallelism=4,
            hash_len=32,
            type=variant
        )
        elapsed = time.time() - start
        print(f"{desc}: {elapsed:.3f}s")
        print(f"  Hash: {hash_result.hex()}\n")
    
    # Demonstrate parameter effects
    print("Parameter Effects on Argon2id:\n")
    
    # Memory cost variation
    print("Memory Cost Variation:")
    for memory_mb in [64, 128, 256, 512]:
        memory_cost = memory_mb * 1024  # Convert MB to KB
        start = time.time()
        hash_result = hash_secret_raw(
            secret=password,
            salt=salt,
            time_cost=3,
            memory_cost=memory_cost,
            parallelism=4,
            hash_len=32,
            type=Type.ID
        )
        elapsed = time.time() - start
        print(f"  {memory_mb:3d}MB: {elapsed:.3f}s")
    
    # High-level API usage (recommended)
    print("\nRecommended High-Level API Usage:")
    
    # Create a password hasher with secure defaults
    ph = PasswordHasher(
        time_cost=3,          # Iterations
        memory_cost=65536,    # 64MB
        parallelism=4,        # Threads
        hash_len=32,
        salt_len=16,
        type=Type.ID
    )
    
    # Hash password
    hash_str = ph.hash("MySecurePassword")
    print(f"Hashed: {hash_str}")
    
    # Verify password
    try:
        ph.verify(hash_str, "MySecurePassword")
        print("Password verified successfully!")
    except argon2.exceptions.VerifyMismatchError:
        print("Password verification failed!")
    
    # Check if rehashing needed (parameters changed)
    if ph.check_needs_rehash(hash_str):
        print("Hash needs rehashing with updated parameters")
    
    # Demonstrate format parsing
    print(f"\nHash Format Breakdown:")
    parts = hash_str.split('$')
    print(f"  Algorithm: {parts[1]}")
    print(f"  Version: {parts[2]}")
    print(f"  Parameters: {parts[3]}")
    print(f"  Salt: {parts[4]}")
    print(f"  Hash: {parts[5]}")

demonstrate_argon2()

Argon2id, the recommended variant, combines Argon2i's resistance to side-channel attacks with Argon2d's resistance to GPU cracking attacks. It uses data-independent memory access for the first half-pass over memory and data-dependent access for subsequent passes. This hybrid approach provides the best balance for most password hashing applications.