Last active
May 7, 2024 14:24
-
-
Save 84adam/51b30ef4b8edd420f6f1d6ab6eea40fe to your computer and use it in GitHub Desktop.
bcrypt/argon2/pbkdf2 hash test -- target a work factor that requires at least 250 ms to compute
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# bcrypt-argon2-pbkdf2-hash-test.py | |
import threading | |
import bcrypt | |
from pwdlib.hashers import argon2 | |
from pwdlib import PasswordHash | |
from pwdlib.hashers.argon2 import Argon2Hasher | |
from hashlib import pbkdf2_hmac | |
import time | |
import psutil | |
from statistics import median | |
def monitor_cpu(): | |
while not stop_thread: | |
global cpu_load | |
cpu_load.append(psutil.cpu_percent(interval=1, percpu=True)) | |
def hash_password(algorithm, work_factor, memory_cost=None): | |
password = b"your_secret_password" | |
start_time = time.time() | |
if algorithm == 'bcrypt': | |
_ = bcrypt.hashpw(password, bcrypt.gensalt(work_factor)) | |
elif algorithm == 'pbkdf2': | |
# Assuming salt is also bytes; normally should be securely generated | |
salt = b'some_random_salt' | |
# pbkdf2_hmac requires the number of iterations, the salt, the desired hash name, and the password | |
_ = pbkdf2_hmac('sha256', password, salt, work_factor) | |
else: | |
# assume argon2 | |
if memory_cost == None: | |
memory_cost = 65536 # 64 MiB; see: https://github.com/hynek/argon2-cffi/blob/0805dbdded04dc3c4bc8573236c80be50ff30113/src/argon2/profiles.py#L30-L38 | |
password_hash = PasswordHash(( | |
Argon2Hasher(time_cost=work_factor, memory_cost=memory_cost), | |
)) | |
_ = password_hash.hash(password) | |
end_time = time.time() | |
if algorithm == 'argon2': | |
print(f"Hashing with {algorithm} work factor of {work_factor} and memory cost of {memory_cost} took {end_time - start_time:.3f} seconds.") | |
else: | |
print(f"Hashing with {algorithm} work factor of {work_factor} took {end_time - start_time:.3f} seconds.") | |
if __name__ == "__main__": | |
# select from: 'pbkdf2', 'argon2' or 'bcrypt' | |
algorithm = 'argon2' | |
# for argon2 | |
memory_cost = 256 * 1024 # 256 MiB; see: https://github.com/sparrowwallet/drongo/blob/master/src/main/java/com/sparrowwallet/drongo/crypto/Argon2KeyDeriver.java | |
min_work = 5 | |
max_work = 30 | |
# for bcrypt | |
if algorithm == 'bcrypt': | |
if max_work > 20: | |
max_work = 20 | |
# for argon2 / bcrypt | |
work_factors = [x for x in range(min_work, max_work+1)] | |
# for pbkdf2 | |
if algorithm == 'pbkdf2': | |
min_work = 700_000 | |
max_work = 1_000_000 | |
work_factors = [x for x in range(min_work, max_work+1, 20_000)] # Increment by 20000 for noticeable differences | |
for work_factor in work_factors: | |
cpu_load = [] | |
stop_thread = False | |
thread = threading.Thread(target=monitor_cpu) | |
thread.start() | |
try: | |
if algorithm in ['bcrypt', 'pbkdf2']: | |
hash_password(algorithm, work_factor) | |
elif algorithm == 'argon2': | |
hash_password(algorithm, work_factor, memory_cost) | |
else: | |
raise Exception(f"algorithm '{algorithm}' not configured") | |
finally: | |
stop_thread = True | |
thread.join() | |
all_max = [] | |
all_avg = [] | |
for index, cpu in enumerate(cpu_load): | |
all_max.append(max(cpu)) | |
all_avg.append(sum(cpu) / len(cpu)) | |
print(f"Max single CPU core usage: {max(all_max):.2f}%") | |
print(f"Average usage all cores during test: {(sum(all_avg) / len(all_avg)):.2f}%\n") |
argon2
example output, 4 core machine, between 10 and 30 iterations:
Hashing with argon2 work factor of 10 took 0.105 seconds.
Max single CPU core usage: 7.40%
Average usage all cores during test: 4.83%
Hashing with argon2 work factor of 11 took 0.134 seconds.
Max single CPU core usage: 6.50%
Average usage all cores during test: 5.45%
Hashing with argon2 work factor of 12 took 0.153 seconds.
Max single CPU core usage: 6.50%
Average usage all cores during test: 5.78%
Hashing with argon2 work factor of 13 took 0.137 seconds.
Max single CPU core usage: 7.60%
Average usage all cores during test: 6.00%
Hashing with argon2 work factor of 14 took 0.157 seconds.
Max single CPU core usage: 7.80%
Average usage all cores during test: 5.60%
Hashing with argon2 work factor of 15 took 0.160 seconds.
Max single CPU core usage: 8.90%
Average usage all cores during test: 6.72%
Hashing with argon2 work factor of 16 took 0.164 seconds.
Max single CPU core usage: 9.00%
Average usage all cores during test: 6.70%
Hashing with argon2 work factor of 17 took 0.180 seconds.
Max single CPU core usage: 8.80%
Average usage all cores during test: 7.00%
Hashing with argon2 work factor of 18 took 0.186 seconds.
Max single CPU core usage: 12.10%
Average usage all cores during test: 8.43%
Hashing with argon2 work factor of 19 took 0.196 seconds.
Max single CPU core usage: 10.20%
Average usage all cores during test: 9.28%
Hashing with argon2 work factor of 20 took 0.214 seconds.
Max single CPU core usage: 10.20%
Average usage all cores during test: 9.20%
Hashing with argon2 work factor of 21 took 0.212 seconds.
Max single CPU core usage: 9.30%
Average usage all cores during test: 7.90%
Hashing with argon2 work factor of 22 took 0.228 seconds.
Max single CPU core usage: 11.60%
Average usage all cores during test: 9.30%
Hashing with argon2 work factor of 23 took 0.233 seconds.
Max single CPU core usage: 11.50%
Average usage all cores during test: 9.85%
Hashing with argon2 work factor of 24 took 0.242 seconds.
Max single CPU core usage: 14.80%
Average usage all cores during test: 10.70%
Hashing with argon2 work factor of 25 took 0.260 seconds.
Max single CPU core usage: 12.90%
Average usage all cores during test: 10.62%
Hashing with argon2 work factor of 26 took 0.265 seconds.
Max single CPU core usage: 11.90%
Average usage all cores during test: 9.55%
Hashing with argon2 work factor of 27 took 0.268 seconds.
Max single CPU core usage: 14.30%
Average usage all cores during test: 11.60%
Hashing with argon2 work factor of 28 took 0.292 seconds.
Max single CPU core usage: 14.10%
Average usage all cores during test: 12.65%
Hashing with argon2 work factor of 29 took 0.286 seconds.
Max single CPU core usage: 13.10%
Average usage all cores during test: 11.95%
Hashing with argon2 work factor of 30 took 0.304 seconds.
Max single CPU core usage: 16.90%
Average usage all cores during test: 13.08%
argon2
with 5-30 work factor, using 256 MiB
of memory on a 4-core machine:
Hashing with argon2 work factor of 5 and memory cost of 262144 took 0.256 seconds.
Max single CPU core usage: 9.80%
Average usage all cores during test: 6.88%
Hashing with argon2 work factor of 6 and memory cost of 262144 took 0.264 seconds.
Max single CPU core usage: 8.60%
Average usage all cores during test: 7.00%
Hashing with argon2 work factor of 7 and memory cost of 262144 took 0.297 seconds.
Max single CPU core usage: 11.70%
Average usage all cores during test: 7.70%
Hashing with argon2 work factor of 8 and memory cost of 262144 took 0.334 seconds.
Max single CPU core usage: 11.80%
Average usage all cores during test: 9.57%
Hashing with argon2 work factor of 9 and memory cost of 262144 took 0.361 seconds.
Max single CPU core usage: 11.00%
Average usage all cores during test: 9.23%
Hashing with argon2 work factor of 10 and memory cost of 262144 took 0.390 seconds.
Max single CPU core usage: 14.10%
Average usage all cores during test: 11.03%
Hashing with argon2 work factor of 11 and memory cost of 262144 took 0.429 seconds.
Max single CPU core usage: 14.70%
Average usage all cores during test: 12.25%
Hashing with argon2 work factor of 12 and memory cost of 262144 took 0.467 seconds.
Max single CPU core usage: 16.40%
Average usage all cores during test: 14.52%
Hashing with argon2 work factor of 13 and memory cost of 262144 took 0.494 seconds.
Max single CPU core usage: 18.00%
Average usage all cores during test: 15.12%
Hashing with argon2 work factor of 14 and memory cost of 262144 took 0.526 seconds.
Max single CPU core usage: 20.30%
Average usage all cores during test: 17.93%
Hashing with argon2 work factor of 15 and memory cost of 262144 took 0.573 seconds.
Max single CPU core usage: 23.60%
Average usage all cores during test: 18.50%
Hashing with argon2 work factor of 16 and memory cost of 262144 took 0.610 seconds.
Max single CPU core usage: 25.00%
Average usage all cores during test: 20.23%
Hashing with argon2 work factor of 17 and memory cost of 262144 took 0.645 seconds.
Max single CPU core usage: 26.40%
Average usage all cores during test: 20.80%
Hashing with argon2 work factor of 18 and memory cost of 262144 took 0.681 seconds.
Max single CPU core usage: 28.60%
Average usage all cores during test: 23.25%
Hashing with argon2 work factor of 19 and memory cost of 262144 took 0.724 seconds.
Max single CPU core usage: 29.20%
Average usage all cores during test: 26.48%
Hashing with argon2 work factor of 20 and memory cost of 262144 took 0.745 seconds.
Max single CPU core usage: 31.80%
Average usage all cores during test: 28.65%
Hashing with argon2 work factor of 21 and memory cost of 262144 took 0.790 seconds.
Max single CPU core usage: 38.50%
Average usage all cores during test: 34.32%
Hashing with argon2 work factor of 22 and memory cost of 262144 took 0.826 seconds.
Max single CPU core usage: 41.00%
Average usage all cores during test: 38.52%
Hashing with argon2 work factor of 23 and memory cost of 262144 took 0.865 seconds.
Max single CPU core usage: 52.80%
Average usage all cores during test: 46.08%
Hashing with argon2 work factor of 24 and memory cost of 262144 took 0.915 seconds.
Max single CPU core usage: 51.70%
Average usage all cores during test: 50.80%
Hashing with argon2 work factor of 25 and memory cost of 262144 took 0.924 seconds.
Max single CPU core usage: 59.30%
Average usage all cores during test: 53.25%
Hashing with argon2 work factor of 26 and memory cost of 262144 took 0.976 seconds.
Max single CPU core usage: 68.00%
Average usage all cores during test: 63.17%
Hashing with argon2 work factor of 27 and memory cost of 262144 took 1.009 seconds.
Max single CPU core usage: 72.70%
Average usage all cores during test: 34.76%
Hashing with argon2 work factor of 28 and memory cost of 262144 took 1.055 seconds.
Max single CPU core usage: 74.10%
Average usage all cores during test: 36.70%
Hashing with argon2 work factor of 29 and memory cost of 262144 took 1.083 seconds.
Max single CPU core usage: 76.00%
Average usage all cores during test: 35.48%
Hashing with argon2 work factor of 30 and memory cost of 262144 took 1.105 seconds.
Max single CPU core usage: 76.90%
Average usage all cores during test: 36.41%
added pbkdf2
-- example output on a 4-core machine:
Hashing with pbkdf2 work factor of 700000 took 0.256 seconds.
Max single CPU core usage: 18.70%
Average usage all cores during test: 2.59%
Hashing with pbkdf2 work factor of 720000 took 0.264 seconds.
Max single CPU core usage: 24.70%
Average usage all cores during test: 6.14%
Hashing with pbkdf2 work factor of 740000 took 0.288 seconds.
Max single CPU core usage: 20.20%
Average usage all cores during test: 7.15%
Hashing with pbkdf2 work factor of 760000 took 0.280 seconds.
Max single CPU core usage: 20.90%
Average usage all cores during test: 2.99%
Hashing with pbkdf2 work factor of 780000 took 0.289 seconds.
Max single CPU core usage: 22.80%
Average usage all cores during test: 3.23%
Hashing with pbkdf2 work factor of 800000 took 0.292 seconds.
Max single CPU core usage: 23.70%
Average usage all cores during test: 3.21%
Hashing with pbkdf2 work factor of 820000 took 0.298 seconds.
Max single CPU core usage: 23.10%
Average usage all cores during test: 3.26%
Hashing with pbkdf2 work factor of 840000 took 0.310 seconds.
Max single CPU core usage: 29.60%
Average usage all cores during test: 4.09%
Hashing with pbkdf2 work factor of 860000 took 0.316 seconds.
Max single CPU core usage: 24.20%
Average usage all cores during test: 3.27%
Hashing with pbkdf2 work factor of 880000 took 0.326 seconds.
Max single CPU core usage: 25.00%
Average usage all cores during test: 3.75%
Hashing with pbkdf2 work factor of 900000 took 0.331 seconds.
Max single CPU core usage: 23.90%
Average usage all cores during test: 3.24%
Hashing with pbkdf2 work factor of 920000 took 0.338 seconds.
Max single CPU core usage: 25.80%
Average usage all cores during test: 3.73%
Hashing with pbkdf2 work factor of 940000 took 0.345 seconds.
Max single CPU core usage: 26.70%
Average usage all cores during test: 3.73%
Hashing with pbkdf2 work factor of 960000 took 0.355 seconds.
Max single CPU core usage: 28.60%
Average usage all cores during test: 4.33%
Hashing with pbkdf2 work factor of 980000 took 0.363 seconds.
Max single CPU core usage: 31.20%
Average usage all cores during test: 4.29%
Hashing with pbkdf2 work factor of 1000000 took 0.364 seconds.
Max single CPU core usage: 27.60%
Average usage all cores during test: 4.08%
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
python3 bcrypt-hash-test.py
; example output on a 4-core machine:NOTE: Max single core usage results may vary; run multiple times to get a more complete sense of CPU utilization.
SUGGESTION: Target a work factor that requires at least 250 ms to compute.
SEE 'Optimal bcrypt work factor' for details; particularly this comment: https://stackoverflow.com/a/61304956