Argon2Argon2 was designed Alex Biryukov, Daniel Dinu, and Dmitry Khovratovich is a key derivation function (KDF), where were we can create hashed values of passwords, or create encryption keys based on a password. It was a winner of the Password Hashing Competition in July 2015, and is robust against GPU and side channel attacks [paper]: |
Outline
Did you know that every eight character password (in lowercase) is cracked by brute force within less than 10 seconds? And if you use a 9 character password it takes less than 10 minutes to crack them (using lowercase letters)? Adding a number to the end, or making the first letter uppercase makes very little difference in the cracking challenge [here]
Here is me using an 8 GPU instance in the Amazon Cloud:
Thus we need to give up on our traditional ways of generating encryption keys or in creating hashed versions of passwords, as our passwords are often guessable. For MD5 we can now get billions or even trillions or hashes per second, where even 9 or 10 character passwords can be cracked for a reasonable financial cost. This includes salting of the password, as the salt is contained with the hashed password, and can be easily cracked with brute force (or dictionaries).
The alternative is to use a hashing method which has a cost in memory, and for CPU processing. We also want to create a method which makes it difficult to apply parallel threads (and thus run it on GPUs). So step forward Argon2, which was designed Alex Biryukov, Daniel Dinu, and Dmitry Khovratovich as a key derivation function. It was a winner of the Password Hashing Competition in July 2015, and is robust against GPU attacks.
It is resistant to GPU attacks, and also has a memory cost. The costs include: execution time (CPU cost); memory required (memory cost); and degree of parallelism.
The parameters include:
- Password (P): Defines the password bytes to be hashed
- Salt (S): Defines the bytes to be used for salting.
- Parallelism (p): Defines the number of thread that are required for the parallelism.
- TagLength (T): Define the number of bytes to return for the hash.
- MemorySizeKB (m): Amount of memory (in KB) to use.
A sample run is:
Message: abc Hash: $argon2i$v=19$m=8,t=1,p=1$x/3yHil0MIE$RDL1Jw Verified: True
A sample benchmark using n (for time cost), p (for parallelism) is:
We can see that for an n value of 128, that the time to compute a hash value is 0.105 seconds (which would give a hashing function of just 10 hashes per second), but for 8,192 it is 5.78 seconds (which is probably too long for a user login). We must thus select a cost value which gives a reasonable time to compute, but is also robust against attacks. The message (m) is defined in log2(m), and where, in this case, m is \(2^8\) bytes [full test].
An outline of the Python code is:
import sys from binascii import unhexlify,hexlify,b2a_base64 import hashlib import hmac import binascii def hkdf_extract(salt, input_key_material, hash=hashlib.sha512): hash_len = hash().digest_size if salt == None or len(salt) == 0: salt = chr(0) * hash_len return hmac.new(salt, input_key_material, hash).digest() def hkdf_expand(pseudo_random_key, info="", length=32, hash=hashlib.sha512): hash_len = hash().digest_size length = int(length) blocks_needed = length // hash_len + (0 if length % hash_len == 0 else 1) okm = [] output_block = "" for counter in range(blocks_needed): inp=output_block + info + chr(counter + 1) output_block = hmac.new(pseudo_random_key,inp.encode() , hash).digest() okm.extend(output_block) return okm[:length] password='hello' salt="8e94ef805b93e683ff18" prk = hkdf_extract(unhexlify(salt), password.encode()) key = hkdf_expand(prk, "", 16) print("Message:\t",password) print("Salt:\t\t",salt) print("===================") print("Key (Hex):\t\t",binascii.hexlify(bytearray(key)).decode()) print("Key (Base-64):\t\t",(b2a_base64(bytearray(key))).decode())