The Right Way To Hash a Password or Create an Encryption Key: PBKDF2, bcrypt and scrypt

In computing, we often need things to be fast, but sometimes we need to slow things down and create a problem that cannot be scaled onto…

Photo by Joel M Mathey on Unsplash

The Right Way To Hash a Password or Create an Encryption Key: PBKDF2, bcrypt and scrypt

In computing, we often need things to be fast, but sometimes we need to slow things down and create a problem that cannot be scaled onto parallel processors. One of these applications is in the hashing of a password. The fast hash crackers can process SHA-1 and SHA-256 hashes at rates that can be over 1 terahashes per second. That’s 1,000 billion passwords tried every second. These crackers often run on Cloud-based systems and use GPUs with over 4,000 cores.

In many applications, we need to generate an encryption key. For this, we could create a random key, but we would need to store it, and where it could be discovered. A typical alternative method is thus to use a Key Derivation Function (KDF) with a salt value in order to generate a hashed value and then use this hash to generate the encryption key. As this method would be open to brute force, we often use a method that will slow the hashing process down, and defeat a hash cracker using parallel processing. This increased difficulity is normally implemented using a hashing loop, and where we hash over a number of rounds. Typical methods are PBKDF2 (Password-Based Key Derivation Function 2), bcrypt and scrypt, as these are more robust against default hash crackers. A sample run here shows some of the speeds:

Method:   Hashes per second
SHA-1: 588235
SHA-256: 602409
SHA-512: 75216
MD5: 606060
SHA-3 (224-bit): 331674
DES: 396
Bcrypt: 215
APR1: 692
PBKDF2 (SHA1): 4884
PBKDF2 (SHA-256): 9648

LM Hash: 2480
NT Hash: 33112
MS DCC: 53361
LDAP (MD5): 11928
LDAP (SHA1): 48828
MS SQL 2000: 16654
MySQL: 30769
Oracle 10: 201
Postgres (MD5): 23900
Cisco PIX: 16297
Cisco Type 7: 16223
Murmur: 2499999

The following implements these methods with a given salt value, along with HKDF, and which is not recommended for generating encryption keys [here]:

import binascii
from Crypto.Protocol.KDF import PBKDF2, scrypt,HKDF
import bcrypt
from Crypto.Hash import SHA256
from Crypto.Random import get_random_bytes
import sys

password="qwerty"
salt = get_random_bytes(16)
s=""
type=1
bytes=16

if (len(sys.argv)>1):
password=str(sys.argv[1])
if (len(sys.argv)>2):
s=str(sys.argv[2])
if (len(sys.argv)>3):
type=int(sys.argv[3])
if (len(sys.argv)>4):
bytes=int(sys.argv[4])


salt=binascii.unhexlify(s)

if (type==1):
KEK = PBKDF2(password, salt, bytes, count=1000, hmac_hash_module=SHA256)
print ("Using PBKDF2")
elif (type==2):
KEK = scrypt(password, salt, bytes, N=2**14, r=8, p=1)
print ("Using scrypt")
elif (type==3):
KEK = bcrypt.kdf(password=password.encode(),salt=b'salt',desired_key_bytes=bytes,rounds=100)
print ("Using bcrypt")
else:
KEK = HKDF(password.encode(), bytes, salt, SHA256, 1)
print ("Using HKDF")


print (f"Password: {password}, Salt: {s}")
print ("\nHash: ",binascii.hexlify(KEK))

A sample run is [here]:

Using scrypt
Password: qwerty123, Salt: 329b074c0058ccf1ba2e4705382963ff

Hash: b'798557bf07a52c2f84c5882a19c1de0ef5bbcbedd9baa34356ab70d2efae8d26'

And what about the wi-fi connection you are using? Well, that uses PBKDF2, so thank the tortoise for your security. A running version of the Python code is here: