Argon2

password hashingsecuritycryptographyauthenticationPythonNode.jsJavaGoRustPHP

Password Hashing Library

Argon2

Overview

Argon2 is the latest key derivation function selected as the winner of the 2015 Password Hashing Competition (PHC) and has established itself as the industry standard for password hashing as of 2025. With memory-hard design, it provides high resistance against parallel attacks using GPUs and ASICs, and responds to various security requirements through configurable parameters. It offers three variants: Argon2d, Argon2i, and Argon2id, allowing optimal implementation selection based on use cases. Rich implementations in major programming languages such as Python, Node.js, Java, Go, Rust, and PHP enable adoption in any application environment. Recommended by major security organizations including OWASP, NIST, and ENISA, it has been widely adopted as the foundational technology for password security in modern web applications.

Details

Argon2 is designed as a memory-hard function, making it difficult for attackers to perform parallel cracking attacks using GPUs and ASICs. By adjusting three parameters - memory usage, parallelism, and execution time - optimization according to hardware environment and security requirements is possible. Argon2d is fast and strong against GPU attacks through data-dependent memory access, while Argon2i is resistant to side-channel attacks through data-independent memory access. Argon2id provides balanced security as a hybrid of both. With automatic salt generation, configurable costs, and cross-platform support, it meets enterprise-level security requirements.

Key Features

  • Memory-Hard Design: High resistance against GPU/ASIC parallel attacks
  • Three Variants: Argon2d, Argon2i, Argon2id for use case optimization
  • Configurable Parameters: Adjustable memory, parallelism, and time cost
  • Multi-Language Support: Implementations in Python, Node.js, Java, Go, Rust, PHP, etc.
  • Industry Standard Certification: Security standard recommended by OWASP, NIST, ENISA
  • Cross-Platform: Stable operation on Windows, Linux, macOS, etc.

Advantages and Disadvantages

Advantages

  • Provides industry-leading security as PHC winner
  • High resistance against modern attack methods through memory-hard design
  • Supports diverse environments and security requirements with configurable parameters
  • Rich language implementations enable adoption without platform constraints
  • Reliability guaranteed through recommendations from major security organizations
  • Flexible design capable of adapting to future hardware advances

Disadvantages

  • Higher memory usage and CPU load compared to traditional hash functions
  • Parameter tuning requires deep understanding of cryptography and hardware performance
  • Difficult to use in memory-constrained environments (embedded systems, etc.)
  • Requires planned approach for migration from legacy systems
  • Potential for performance issues or security degradation with improper parameter settings
  • Implementation may be limited on some older platforms

Reference Pages

Usage Examples

Python Implementation (using argon2-cffi)

# requirements.txt
# argon2-cffi==23.1.0

import argon2
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError, HashingError

class SecurePasswordManager:
    def __init__(self):
        # Initialize PasswordHasher with recommended parameters
        self.ph = PasswordHasher(
            time_cost=3,        # 3 iterations (recommended minimum)
            memory_cost=65536,  # 64 MiB memory usage
            parallelism=1,      # 1 thread (adjust as needed)
            hash_len=32,        # 32-byte hash output
            salt_len=16         # 16-byte salt
        )
    
    def hash_password(self, password: str) -> str:
        """Hash password with Argon2"""
        try:
            # Hash password (salt auto-generated)
            hash_value = self.ph.hash(password)
            print(f"Password hash generation successful")
            return hash_value
        except HashingError as e:
            print(f"Hashing error: {e}")
            raise
    
    def verify_password(self, hash_value: str, password: str) -> bool:
        """Verify password against hash"""
        try:
            self.ph.verify(hash_value, password)
            print("Password verification successful")
            return True
        except VerifyMismatchError:
            print("Password verification failed")
            return False
        except Exception as e:
            print(f"Verification error: {e}")
            raise
    
    def check_needs_rehash(self, hash_value: str) -> bool:
        """Check if hash needs to be recalculated"""
        return self.ph.check_needs_rehash(hash_value)

# Usage example
password_manager = SecurePasswordManager()

# During user registration
user_password = "secure_password_123!"
password_hash = password_manager.hash_password(user_password)
print(f"Generated hash: {password_hash}")

# During login
if password_manager.verify_password(password_hash, user_password):
    print("Login successful")
else:
    print("Login failed")

# Hash upgrade check
if password_manager.check_needs_rehash(password_hash):
    print("Hash recalculation recommended")
    new_hash = password_manager.hash_password(user_password)
    print(f"New hash: {new_hash}")

Node.js Implementation (using argon2 package)

// package.json dependencies
// "argon2": "^0.31.2"

const argon2 = require('argon2');

class NodePasswordManager {
    constructor() {
        // Argon2id configuration (recommended variant)
        this.options = {
            type: argon2.argon2id,  // Use Argon2id
            memoryCost: 2 ** 16,    // 64 MiB
            timeCost: 3,            // 3 iterations
            parallelism: 1,         // 1 parallel thread
            hashLength: 32,         // 32-byte output
            saltLength: 16,         // 16-byte salt
        };
    }

    async hashPassword(password) {
        try {
            console.log('Starting password hashing...');
            const hash = await argon2.hash(password, this.options);
            console.log('Hashing completed');
            return hash;
        } catch (error) {
            console.error('Hashing error:', error);
            throw error;
        }
    }

    async verifyPassword(hash, password) {
        try {
            console.log('Starting password verification...');
            const isValid = await argon2.verify(hash, password);
            console.log(`Verification result: ${isValid ? 'success' : 'failed'}`);
            return isValid;
        } catch (error) {
            console.error('Verification error:', error);
            return false;
        }
    }

    async needsRehash(hash) {
        try {
            return await argon2.needsRehash(hash, this.options);
        } catch (error) {
            console.error('Rehash check error:', error);
            return false;
        }
    }

    // Express.js middleware example
    createAuthMiddleware() {
        return async (req, res, next) => {
            const { password, hash } = req.body;
            
            if (!password || !hash) {
                return res.status(400).json({ 
                    error: 'Password and hash are required' 
                });
            }

            try {
                const isValid = await this.verifyPassword(hash, password);
                if (isValid) {
                    // Hash upgrade check
                    if (await this.needsRehash(hash)) {
                        const newHash = await this.hashPassword(password);
                        req.newHash = newHash;
                    }
                    next();
                } else {
                    res.status(401).json({ error: 'Authentication failed' });
                }
            } catch (error) {
                res.status(500).json({ error: 'Authentication error' });
            }
        };
    }
}

// Usage example
async function main() {
    const passwordManager = new NodePasswordManager();
    
    const password = 'user_secure_password_2025!';
    
    // Password hashing
    const hash = await passwordManager.hashPassword(password);
    console.log('Generated hash:', hash);
    
    // Password verification
    const isValid = await passwordManager.verifyPassword(hash, password);
    console.log('Verification result:', isValid);
    
    // Wrong password test
    const isInvalid = await passwordManager.verifyPassword(hash, 'wrong_password');
    console.log('Wrong password result:', isInvalid);
    
    // Rehash necessity check
    const needsRehash = await passwordManager.needsRehash(hash);
    console.log('Needs rehash:', needsRehash);
}

main().catch(console.error);

Java Implementation (using argon2-jvm)

// build.gradle or pom.xml
// implementation 'de.mkammerer:argon2-jvm:2.12'

import de.mkammerer.argon2.Argon2;
import de.mkammerer.argon2.Argon2Factory;
import de.mkammerer.argon2.Argon2Helper;

public class JavaPasswordManager {
    private final Argon2 argon2;
    private final int iterations;
    private final int memory;
    private final int parallelism;

    public JavaPasswordManager() {
        this.argon2 = Argon2Factory.create();
        this.memory = 65536;      // 64 MiB
        this.parallelism = 1;     // 1 parallel thread
        
        // Dynamically calculate optimal iterations (complete within 1 second)
        this.iterations = Argon2Helper.findIterations(
            argon2, 1000, memory, parallelism
        );
        
        System.out.println("Optimal iterations: " + iterations);
    }

    public String hashPassword(String password) {
        char[] passwordChars = password.toCharArray();
        try {
            System.out.println("Starting password hashing...");
            String hash = argon2.hash(iterations, memory, parallelism, passwordChars);
            System.out.println("Hashing completed");
            return hash;
        } finally {
            // Wipe confidential data for security
            argon2.wipeArray(passwordChars);
        }
    }

    public boolean verifyPassword(String hash, String password) {
        char[] passwordChars = password.toCharArray();
        try {
            System.out.println("Starting password verification...");
            boolean isValid = argon2.verify(hash, passwordChars);
            System.out.println("Verification result: " + (isValid ? "success" : "failed"));
            return isValid;
        } finally {
            // Wipe confidential data for security
            argon2.wipeArray(passwordChars);
        }
    }

    public void close() {
        // Resource cleanup
        if (argon2 != null) {
            // Cleanup process if needed
        }
    }

    // Spring Security integration example
    public static class Argon2PasswordEncoder 
            implements org.springframework.security.crypto.password.PasswordEncoder {
        
        private final JavaPasswordManager passwordManager;

        public Argon2PasswordEncoder() {
            this.passwordManager = new JavaPasswordManager();
        }

        @Override
        public String encode(CharSequence rawPassword) {
            return passwordManager.hashPassword(rawPassword.toString());
        }

        @Override
        public boolean matches(CharSequence rawPassword, String encodedPassword) {
            return passwordManager.verifyPassword(encodedPassword, rawPassword.toString());
        }
    }

    // Usage example
    public static void main(String[] args) {
        JavaPasswordManager manager = new JavaPasswordManager();
        
        try {
            String password = "enterprise_password_2025!";
            
            // Password hashing
            String hash = manager.hashPassword(password);
            System.out.println("Generated hash: " + hash);
            
            // Password verification
            boolean isValid = manager.verifyPassword(hash, password);
            System.out.println("Verification success: " + isValid);
            
            // Wrong password test
            boolean isInvalid = manager.verifyPassword(hash, "wrong_password");
            System.out.println("Wrong password: " + isInvalid);
            
        } finally {
            manager.close();
        }
    }
}

Go Implementation (using golang.org/x/crypto/argon2)

// go.mod
// require golang.org/x/crypto v0.17.0

package main

import (
    "crypto/rand"
    "crypto/subtle"
    "encoding/base64"
    "errors"
    "fmt"
    "strings"

    "golang.org/x/crypto/argon2"
)

type Argon2Params struct {
    Memory      uint32
    Iterations  uint32
    Parallelism uint8
    SaltLength  uint32
    KeyLength   uint32
}

type GoPasswordManager struct {
    params Argon2Params
}

func NewGoPasswordManager() *GoPasswordManager {
    return &GoPasswordManager{
        params: Argon2Params{
            Memory:      64 * 1024, // 64 MiB
            Iterations:  3,         // 3 iterations
            Parallelism: 1,         // 1 parallel thread
            SaltLength:  16,        // 16-byte salt
            KeyLength:   32,        // 32-byte key
        },
    }
}

func (pm *GoPasswordManager) HashPassword(password string) (string, error) {
    fmt.Println("Starting password hashing...")
    
    // Generate random salt
    salt := make([]byte, pm.params.SaltLength)
    if _, err := rand.Read(salt); err != nil {
        return "", fmt.Errorf("salt generation error: %w", err)
    }

    // Compute hash with Argon2id
    hash := argon2.IDKey(
        []byte(password),
        salt,
        pm.params.Iterations,
        pm.params.Memory,
        pm.params.Parallelism,
        pm.params.KeyLength,
    )

    // Encoding format: $argon2id$v=19$m=65536,t=3,p=1$salt$hash
    encodedHash := fmt.Sprintf(
        "$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s",
        argon2.Version,
        pm.params.Memory,
        pm.params.Iterations,
        pm.params.Parallelism,
        base64.RawStdEncoding.EncodeToString(salt),
        base64.RawStdEncoding.EncodeToString(hash),
    )

    fmt.Println("Hashing completed")
    return encodedHash, nil
}

func (pm *GoPasswordManager) VerifyPassword(encodedHash, password string) (bool, error) {
    fmt.Println("Starting password verification...")
    
    // Parse hash
    params, salt, hash, err := pm.decodeHash(encodedHash)
    if err != nil {
        return false, fmt.Errorf("hash decode error: %w", err)
    }

    // Recompute hash with input password
    computedHash := argon2.IDKey(
        []byte(password),
        salt,
        params.Iterations,
        params.Memory,
        params.Parallelism,
        params.KeyLength,
    )

    // Constant-time comparison to prevent timing attacks
    isValid := subtle.ConstantTimeCompare(hash, computedHash) == 1
    fmt.Printf("Verification result: %t\n", isValid)
    return isValid, nil
}

func (pm *GoPasswordManager) decodeHash(encodedHash string) (*Argon2Params, []byte, []byte, error) {
    parts := strings.Split(encodedHash, "$")
    if len(parts) != 6 {
        return nil, nil, nil, errors.New("invalid hash format")
    }

    if parts[1] != "argon2id" {
        return nil, nil, nil, errors.New("unsupported Argon2 variant")
    }

    var version int
    if _, err := fmt.Sscanf(parts[2], "v=%d", &version); err != nil {
        return nil, nil, nil, err
    }

    params := &Argon2Params{}
    if _, err := fmt.Sscanf(parts[3], "m=%d,t=%d,p=%d", 
        &params.Memory, &params.Iterations, &params.Parallelism); err != nil {
        return nil, nil, nil, err
    }

    salt, err := base64.RawStdEncoding.DecodeString(parts[4])
    if err != nil {
        return nil, nil, nil, err
    }

    hash, err := base64.RawStdEncoding.DecodeString(parts[5])
    if err != nil {
        return nil, nil, nil, err
    }

    params.SaltLength = uint32(len(salt))
    params.KeyLength = uint32(len(hash))

    return params, salt, hash, nil
}

// HTTP handler example
func (pm *GoPasswordManager) AuthenticationHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        http.Error(w, "Only POST method allowed", http.StatusMethodNotAllowed)
        return
    }

    var authReq struct {
        Password string `json:"password"`
        Hash     string `json:"hash"`
    }

    if err := json.NewDecoder(r.Body).Decode(&authReq); err != nil {
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }

    isValid, err := pm.VerifyPassword(authReq.Hash, authReq.Password)
    if err != nil {
        http.Error(w, "Authentication error", http.StatusInternalServerError)
        return
    }

    response := map[string]interface{}{
        "authenticated": isValid,
        "timestamp":     time.Now().Unix(),
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(response)
}

// Usage example
func main() {
    manager := NewGoPasswordManager()
    
    password := "golang_secure_2025!"
    
    // Password hashing
    hash, err := manager.HashPassword(password)
    if err != nil {
        fmt.Printf("Hashing error: %v\n", err)
        return
    }
    fmt.Printf("Generated hash: %s\n", hash)
    
    // Password verification
    isValid, err := manager.VerifyPassword(hash, password)
    if err != nil {
        fmt.Printf("Verification error: %v\n", err)
        return
    }
    fmt.Printf("Verification success: %t\n", isValid)
    
    // Wrong password test
    isInvalid, err := manager.VerifyPassword(hash, "wrong_password")
    if err != nil {
        fmt.Printf("Verification error: %v\n", err)
        return
    }
    fmt.Printf("Wrong password: %t\n", isInvalid)
}

Rust Implementation (using argon2 crate)

// Cargo.toml
// [dependencies]
// argon2 = "0.5.2"
// rand = "0.8.5"
// thiserror = "1.0.50"

use argon2::{
    Argon2, PasswordHash, PasswordHasher, PasswordVerifier, 
    Algorithm, Version, Params
};
use rand::{rngs::OsRng, RngCore};
use std::time::Instant;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum PasswordError {
    #[error("Hashing error: {0}")]
    HashingError(#[from] argon2::password_hash::Error),
    #[error("Salt generation error")]
    SaltGenerationError,
    #[error("Password verification failed")]
    VerificationFailed,
}

pub struct RustPasswordManager {
    argon2: Argon2<'static>,
    params: Params,
}

impl RustPasswordManager {
    pub fn new() -> Result<Self, PasswordError> {
        println!("Initializing Rust password manager...");
        
        // Argon2id parameter configuration
        let params = Params::new(
            65536,  // 64 MiB memory cost
            3,      // 3 iterations
            1,      // 1 parallel thread
            Some(32) // 32-byte hash output
        ).map_err(PasswordError::HashingError)?;

        let argon2 = Argon2::new(
            Algorithm::Argon2id,
            Version::V0x13,
            params.clone(),
        );

        Ok(Self { argon2, params })
    }

    pub fn hash_password(&self, password: &str) -> Result<String, PasswordError> {
        println!("Starting password hashing...");
        let start = Instant::now();

        // Generate salt
        let mut salt = [0u8; 16];
        OsRng.fill_bytes(&mut salt);

        // Hash password
        let password_hash = self.argon2
            .hash_password(password.as_bytes(), &salt)
            .map_err(PasswordError::HashingError)?;

        let duration = start.elapsed();
        println!("Hashing completed (elapsed time: {:?})", duration);

        Ok(password_hash.to_string())
    }

    pub fn verify_password(&self, hash: &str, password: &str) -> Result<bool, PasswordError> {
        println!("Starting password verification...");
        let start = Instant::now();

        // Parse hash
        let parsed_hash = PasswordHash::new(hash)
            .map_err(PasswordError::HashingError)?;

        // Verify password
        let result = self.argon2
            .verify_password(password.as_bytes(), &parsed_hash);

        let duration = start.elapsed();
        
        match result {
            Ok(()) => {
                println!("Verification successful (elapsed time: {:?})", duration);
                Ok(true)
            }
            Err(_) => {
                println!("Verification failed (elapsed time: {:?})", duration);
                Ok(false)
            }
        }
    }

    pub fn get_params_info(&self) -> String {
        format!(
            "Argon2id - Memory: {} KiB, Iterations: {}, Parallelism: {}",
            self.params.m_cost(),
            self.params.t_cost(),
            self.params.p_cost()
        )
    }
}

// Async web framework (Axum) integration example
#[cfg(feature = "axum")]
use axum::{
    extract::Json,
    http::StatusCode,
    response::Json as ResponseJson,
    routing::post,
    Router,
};

#[cfg(feature = "axum")]
#[derive(serde::Deserialize)]
struct AuthRequest {
    password: String,
    hash: String,
}

#[cfg(feature = "axum")]
#[derive(serde::Serialize)]
struct AuthResponse {
    authenticated: bool,
    message: String,
    timestamp: u64,
}

#[cfg(feature = "axum")]
async fn authenticate_password(
    Json(payload): Json<AuthRequest>,
) -> Result<ResponseJson<AuthResponse>, StatusCode> {
    let password_manager = RustPasswordManager::new()
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    match password_manager.verify_password(&payload.hash, &payload.password) {
        Ok(is_valid) => {
            let response = AuthResponse {
                authenticated: is_valid,
                message: if is_valid { 
                    "Authentication successful".to_string() 
                } else { 
                    "Authentication failed".to_string() 
                },
                timestamp: std::time::SystemTime::now()
                    .duration_since(std::time::UNIX_EPOCH)
                    .unwrap()
                    .as_secs(),
            };
            Ok(ResponseJson(response))
        }
        Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
    }
}

#[cfg(feature = "axum")]
fn create_app() -> Router {
    Router::new()
        .route("/authenticate", post(authenticate_password))
}

// Usage example and testing
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let password_manager = RustPasswordManager::new()?;
    
    println!("Configuration info: {}", password_manager.get_params_info());
    
    let password = "rust_secure_password_2025!";
    
    // Password hashing
    let hash = password_manager.hash_password(password)?;
    println!("Generated hash: {}", hash);
    
    // Password verification
    let is_valid = password_manager.verify_password(&hash, password)?;
    println!("Verification success: {}", is_valid);
    
    // Wrong password test
    let is_invalid = password_manager.verify_password(&hash, "wrong_password")?;
    println!("Wrong password: {}", is_invalid);
    
    // Performance test
    println!("\nStarting performance test...");
    let iterations = 5;
    let mut total_time = std::time::Duration::new(0, 0);
    
    for i in 1..=iterations {
        let start = Instant::now();
        let test_hash = password_manager.hash_password(&format!("test_password_{}", i))?;
        let duration = start.elapsed();
        total_time += duration;
        
        println!("Test {}: {:?}", i, duration);
    }
    
    let average_time = total_time / iterations;
    println!("Average hashing time: {:?}", average_time);
    
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_password_hashing_and_verification() {
        let manager = RustPasswordManager::new().unwrap();
        let password = "test_password_123!";
        
        let hash = manager.hash_password(password).unwrap();
        assert!(manager.verify_password(&hash, password).unwrap());
        assert!(!manager.verify_password(&hash, "wrong_password").unwrap());
    }

    #[test]
    fn test_different_passwords_different_hashes() {
        let manager = RustPasswordManager::new().unwrap();
        
        let hash1 = manager.hash_password("password1").unwrap();
        let hash2 = manager.hash_password("password2").unwrap();
        
        assert_ne!(hash1, hash2);
    }

    #[test]
    fn test_same_password_different_hashes() {
        let manager = RustPasswordManager::new().unwrap();
        let password = "same_password";
        
        let hash1 = manager.hash_password(password).unwrap();
        let hash2 = manager.hash_password(password).unwrap();
        
        // Different hashes generated due to different salts
        assert_ne!(hash1, hash2);
        
        // Both verify successfully with the same password
        assert!(manager.verify_password(&hash1, password).unwrap());
        assert!(manager.verify_password(&hash2, password).unwrap());
    }
}

PHP Implementation (PHP 7.2+ Native Support)

<?php
// PHP 7.2+ with native Argon2 support

class PHPPasswordManager {
    private $options;
    
    public function __construct() {
        // Argon2id recommended configuration
        $this->options = [
            'memory_cost' => 65536,  // 64 MiB
            'time_cost' => 3,        // 3 iterations
            'threads' => 1,          // 1 parallel thread
        ];
        
        echo "PHP password manager initialization completed\n";
        echo "Configuration: " . json_encode($this->options) . "\n";
    }
    
    public function hashPassword($password) {
        echo "Starting password hashing...\n";
        $start = microtime(true);
        
        try {
            // Use PASSWORD_ARGON2ID (PHP 7.2+)
            $hash = password_hash($password, PASSWORD_ARGON2ID, $this->options);
            
            $duration = (microtime(true) - $start) * 1000;
            echo "Hashing completed (elapsed time: {$duration}ms)\n";
            
            return $hash;
        } catch (Exception $e) {
            echo "Hashing error: " . $e->getMessage() . "\n";
            throw $e;
        }
    }
    
    public function verifyPassword($hash, $password) {
        echo "Starting password verification...\n";
        $start = microtime(true);
        
        try {
            $isValid = password_verify($password, $hash);
            
            $duration = (microtime(true) - $start) * 1000;
            echo "Verification result: " . ($isValid ? "success" : "failed") . " (elapsed time: {$duration}ms)\n";
            
            return $isValid;
        } catch (Exception $e) {
            echo "Verification error: " . $e->getMessage() . "\n";
            return false;
        }
    }
    
    public function needsRehash($hash) {
        return password_needs_rehash($hash, PASSWORD_ARGON2ID, $this->options);
    }
    
    public function getInfo($hash) {
        return password_get_info($hash);
    }
    
    // Laravel integration example
    public function laravelHasher() {
        return new class($this->options) extends \Illuminate\Hashing\AbstractHasher {
            private $options;
            
            public function __construct($options) {
                $this->options = $options;
            }
            
            public function make($value, array $options = []) {
                return password_hash($value, PASSWORD_ARGON2ID, array_merge($this->options, $options));
            }
            
            public function check($value, $hashedValue, array $options = []) {
                return password_verify($value, $hashedValue);
            }
            
            public function needsRehash($hashedValue, array $options = []) {
                return password_needs_rehash($hashedValue, PASSWORD_ARGON2ID, array_merge($this->options, $options));
            }
        };
    }
}

// Symfony integration example
class SymfonyArgon2PasswordHasher implements \Symfony\Component\PasswordHasher\PasswordHasherInterface {
    private $options;
    
    public function __construct(array $options = []) {
        $this->options = array_merge([
            'memory_cost' => 65536,
            'time_cost' => 3,
            'threads' => 1,
        ], $options);
    }
    
    public function hash(#[\SensitiveParameter] string $plainPassword): string {
        return password_hash($plainPassword, PASSWORD_ARGON2ID, $this->options);
    }
    
    public function verify(string $hashedPassword, #[\SensitiveParameter] string $plainPassword): bool {
        return password_verify($plainPassword, $hashedPassword);
    }
    
    public function needsRehash(string $hashedPassword): bool {
        return password_needs_rehash($hashedPassword, PASSWORD_ARGON2ID, $this->options);
    }
}

// Usage example
try {
    $passwordManager = new PHPPasswordManager();
    
    $password = 'php_secure_password_2025!';
    
    // Password hashing
    $hash = $passwordManager->hashPassword($password);
    echo "Generated hash: $hash\n";
    
    // Password verification
    $isValid = $passwordManager->verifyPassword($hash, $password);
    echo "Verification success: " . ($isValid ? 'true' : 'false') . "\n";
    
    // Wrong password test
    $isInvalid = $passwordManager->verifyPassword($hash, 'wrong_password');
    echo "Wrong password: " . ($isInvalid ? 'true' : 'false') . "\n";
    
    // Hash information display
    $info = $passwordManager->getInfo($hash);
    echo "Hash information: " . json_encode($info, JSON_PRETTY_PRINT) . "\n";
    
    // Rehash necessity check
    $needsRehash = $passwordManager->needsRehash($hash);
    echo "Needs rehash: " . ($needsRehash ? 'true' : 'false') . "\n";
    
    // Performance test
    echo "\nStarting performance test...\n";
    $iterations = 5;
    $totalTime = 0;
    
    for ($i = 1; $i <= $iterations; $i++) {
        $start = microtime(true);
        $testHash = $passwordManager->hashPassword("test_password_$i");
        $duration = (microtime(true) - $start) * 1000;
        $totalTime += $duration;
        
        echo "Test $i: {$duration}ms\n";
    }
    
    $averageTime = $totalTime / $iterations;
    echo "Average hashing time: {$averageTime}ms\n";
    
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

// Web API example (simple REST API)
function handlePasswordAPI() {
    header('Content-Type: application/json');
    
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        http_response_code(405);
        echo json_encode(['error' => 'Only POST method allowed']);
        return;
    }
    
    $input = json_decode(file_get_contents('php://input'), true);
    
    if (!isset($input['action'])) {
        http_response_code(400);
        echo json_encode(['error' => 'action is required']);
        return;
    }
    
    $passwordManager = new PHPPasswordManager();
    
    try {
        switch ($input['action']) {
            case 'hash':
                if (!isset($input['password'])) {
                    http_response_code(400);
                    echo json_encode(['error' => 'password is required']);
                    return;
                }
                
                $hash = $passwordManager->hashPassword($input['password']);
                echo json_encode([
                    'success' => true,
                    'hash' => $hash,
                    'timestamp' => time()
                ]);
                break;
                
            case 'verify':
                if (!isset($input['password']) || !isset($input['hash'])) {
                    http_response_code(400);
                    echo json_encode(['error' => 'password and hash are required']);
                    return;
                }
                
                $isValid = $passwordManager->verifyPassword($input['hash'], $input['password']);
                echo json_encode([
                    'success' => true,
                    'valid' => $isValid,
                    'timestamp' => time()
                ]);
                break;
                
            default:
                http_response_code(400);
                echo json_encode(['error' => 'invalid action']);
        }
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => 'Server error: ' . $e->getMessage()]);
    }
}

// Enable when using API
// handlePasswordAPI();
?>