import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;

/** Measures how long one hash takes for each algorithm and cost setting. */
public class HashTimings {

    static void time(String label, PasswordEncoder enc) {
        String pw = "correct horse battery staple";
        enc.encode(pw); // warm-up so JIT does not distort the first number
        int runs = 5;
        long total = 0;
        String last = null;
        for (int i = 0; i < runs; i++) {
            long t0 = System.nanoTime();
            last = enc.encode(pw);
            total += System.nanoTime() - t0;
        }
        System.out.printf("%-34s %7.1f ms   hash length=%d%n", label, total / runs / 1e6, last.length());
    }

    public static void main(String[] args) {
        for (int cost : new int[] {10, 11, 12, 13, 14}) {
            time("BCrypt cost=" + cost, new BCryptPasswordEncoder(cost));
        }
        time("PBKDF2-HMAC-SHA256 310k iter",
                Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8());
        time("PBKDF2-HMAC-SHA256 600k iter",
                new Pbkdf2PasswordEncoder("", 16, 600_000, Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256));
        time("Argon2id m=16MiB t=2 p=1", new Argon2PasswordEncoder(16, 32, 1, 16 * 1024, 2));
        time("Argon2id m=19MiB t=2 p=1 (OWASP)", new Argon2PasswordEncoder(16, 32, 1, 19 * 1024, 2));
        time("Argon2id m=64MiB t=3 p=1", new Argon2PasswordEncoder(16, 32, 1, 64 * 1024, 3));
        time("SCrypt N=2^15 r=8 p=1", new SCryptPasswordEncoder(1 << 15, 8, 1, 32, 16));
    }
}
