Key Derivation Functions (KDF): PBKDF2, bcrypt, scrpyt and HKDFIn 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 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. As this method would be open to brute force, we often use a method which will slow the hashing process down, and defeat a hash cracker based on parallel processing. This is normally implemented using a hashing loop, and where we hash over a number of rounds. Typical methods are PBKDF2, bcrypt and scrypt, as these are more robust against default hash crackers. This page implements these methods with a given salt value, along with including HKDF, and which is not recommended for generating encryption keys. |
Outline
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 [here]:
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. The following is the code:
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:
Using scrypt Password: qwerty123, Salt: 329b074c0058ccf1ba2e4705382963ff Hash: b'798557bf07a52c2f84c5882a19c1de0ef5bbcbedd9baa34356ab70d2efae8d26'