Last active
December 10, 2019 16:31
-
-
Save lunaluxie/b1801aa6ff00a457c915f9d9a7437f98 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
import hashlib | |
import random | |
import string | |
def hash_password(password, salt=None, iterations=100000): | |
""" | |
Hash a string using SHA3-512 | |
input: | |
String: password | |
String: salt (use if you are comparing existing hash) | |
If empty, it generates new salt | |
Optional Input: | |
Int: iterations | |
How many times the hash hashes itself | |
should be consistent across your application | |
Return: | |
Tuple with the password hash and salt used | |
Example: (pass_hash, salt) | |
""" | |
# Do type checking | |
if not type(password) == type("String"): | |
raise TypeError("Password should be a string") | |
# if no salt is given | |
# generate 16 alphanumeric long salt using system random | |
if not salt: | |
salt = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(16)) | |
# encode to make it compatible with hashlib algorithm | |
encoded_password = bytes(password, encoding='utf-8') | |
encoded_salt = bytes(salt, encoding='utf-8') | |
pass_hash = hashlib.sha3_512(encoded_password+encoded_salt).hexdigest() | |
# use iterative hashing | |
for _ in range(iterations): | |
pass_hash = hashlib.sha3_512(bytes(pass_hash, encoding="utf-8")+encoded_password).hexdigest() | |
return (pass_hash, salt) | |
def validate_password(password, pw_hash_tuple): | |
""" | |
Check if entered password matches a stored password hash | |
Input: | |
String: password | |
Tuple: pw_hash_tuple | |
of the scheme: (password_hash, salt) | |
Output: | |
Boolean: if they are identical. | |
""" | |
# Do input validation | |
if not len(pw_hash_tuple) == 2: | |
raise ValueError("pw_hash_tuple should have length 2") | |
if not type(password) == type('string'): | |
raise TypeError("password should be a string") | |
for item in pw_hash_tuple: | |
if not type(item) == type("string"): | |
raise TypeError("items in pw_hash_tuple should be strings") | |
stored_pw_hash = pw_hash_tuple[0] | |
stored_pw_salt = pw_hash_tuple[1] | |
# compute the hash of guesspassword using the same salt | |
user_pw_hash_tuple = hash_password(password, salt=stored_pw_salt) | |
# compare the two hashes | |
if user_pw_hash_tuple[0] == stored_pw_hash: | |
return True | |
else: | |
return False | |
pw_hash_tuple = hash_password("123456") | |
guess_password = "123456" | |
print (validate_password(guess_password, pw_hash_tuple)) # True |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Wow! This looks great! But I have a question, why are you not using
Crypto.Random.get_random_bytes
for a random salt? That would make more sense for me security-wise.Edit: Why are you saving the salt per hash? That would be pretty weak