Skip to content

Instantly share code, notes, and snippets.

@84adam
Last active May 7, 2024 14:24
Show Gist options
  • Select an option

  • Save 84adam/51b30ef4b8edd420f6f1d6ab6eea40fe to your computer and use it in GitHub Desktop.

Select an option

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
# 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")
@84adam
Copy link
Author

84adam commented May 6, 2024

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