def _crypt(self, password: str): if self._kdf_memlimit > MEMLIMIT_MAX: raise Error('memlimit too high') opslimit = max(32768, self._kdf_opslimit) n_log2 = 1 r = 8 p = 0 if opslimit < self._kdf_memlimit // 32: maxn = opslimit // (r * 4) p = 1 else: maxn = self._kdf_memlimit // (r * 128) while n_log2 < 63: if 1 << n_log2 > maxn // 2: break n_log2 += 1 if not p: p = min(0x3fffffff, (opslimit // 4) // (1 << n_log2)) // r if n_log2 > N_LOG2_MAX: raise Error('n_log2 too high') self._keynum_sk.xor( scrypt.Scrypt( salt=self._kdf_salt, length=KEYNUM_SK_LEN, n=1 << n_log2, r=r, p=p, ).derive(password.encode()))
def verify_password(password: bytes, password_hash: str) -> bool: """ verify a given password matches the given password hash raises TypeError, ValueError if the input is not well formed returns True if the password matches the hash. otherwise False The output of hash_password contains the parameters and salt as well as the hashed password. This allows for hash_password to be upgraded in the future with better algorithms, while allowing verify_password to be able to verify passwords hashed using old versions. :param password: the user supplied password encoded as bytes :param password_hash: a hash previously determined using hash_password() """ if not isinstance(password, bytes): raise TypeError("expected bytes received %s" % type(password)) if not isinstance(password_hash, str): raise TypeError("expected bytes received %s" % type(password)) digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) digest.update(password) key_material = digest.finalize() parts = password_hash.encode('utf-8').split(b':') kind = parts[0] version = parts[1] params = base64.b64decode(parts[2]) data = base64.b64decode(parts[3]) if kind != b'scrypt' or version != b"1": raise ValueError("invalid method") if version != b"1": raise ValueError("invalid version") e = None try: N, r, p, salt_length, length = struct.unpack(">HBBBB", params) except struct.error as ex: e = ValueError(str(ex)) if e: raise e salt = data[:salt_length] expected = data[salt_length:] kdf = scrypt.Scrypt(salt, length, N, r, p, backend=default_backend()) result = False try: kdf.verify(key_material, expected) result = True except InvalidKey as e: pass return result
def hash_password(password: bytes) -> str: """ hash a password :return: the hashed password The output string contains the parameters used to define the hash, as well as the randomly generated salt used. Implementation notes: This method pre-hashes the password using sha-256. A salt is generated and then it then hashes the output using scrypt with parameters N=16384, r=16, p=1. :param password: the user supplied password encoded as bytes """ if not isinstance(password, bytes): raise TypeError("expected bytes received %s" % type(password)) digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) digest.update(password) key_material = digest.finalize() # N: iteration count # r: block size # p: parallelism factor # Memory required = 128 * N * r * p bytes # Memory required = 128 * 16384 * 16 * 1 bytes # Memory required = 33554432 bytes # Memory required = 32768 KB # Memory required = 32.0 MB N = 16384 r = 16 p = 1 salt = os.urandom(Auth.SALT_LENGTH) params = struct.pack(">HBBBB", N, r, p, Auth.SALT_LENGTH, Auth.DIGEST_LENGTH) # format: # method:version:params:salt+hash header = b"scrypt:1:" + base64.b64encode(params) + b":" kdf = scrypt.Scrypt(salt, Auth.DIGEST_LENGTH, N, r, p, backend=default_backend()) out = kdf.derive(key_material) footer = base64.b64encode(salt + out) return (header + footer).decode("utf-8")
def derive_source_filesystem_id(self, source_passphrase: "DicewarePassphrase") -> str: scrypt_for_filesystem_id = scrypt.Scrypt( length=64, salt=self._salt_for_filesystem_id, n=self._scrypt_n, r=self._scrypt_r, p=self._scrypt_p, backend=self._backend, ) hashed_passphrase = scrypt_for_filesystem_id.derive(source_passphrase.encode("utf-8")) return b32encode(hashed_passphrase).decode("utf-8")
def _scrypt_hash(self, password: str, salt: bytes) -> bytes: backend = default_backend() scrypt_instance = scrypt.Scrypt( length=64, salt=salt, n=2**14, r=8, p=1, backend=backend, ) return scrypt_instance.derive(password.encode("utf-8"))
def authenticate_user_credentials(username, password): from cryptography import exceptions try: import json with open("../../db.json", "r") as f: data = json.load(f) db_user = data["li"]["username"] db_pass = data["li"]["password"] import mysql.connector with mysql.connector.connect( host="localhost", user=db_user, password=db_pass, database="li", raw=True, ) as cnx: cursor = cnx.cursor() cursor.execute( """SELECT salt, hashed_key FROM li_auth WHERE username=%s""", (username, ), ) row = cursor.fetchone() if not row: return False salt = bytes(row[0]) hashed_key = bytes(row[1]) from cryptography.hazmat.primitives.kdf import scrypt kdf = scrypt.Scrypt(salt=salt, length=128, n=2**14, r=8, p=1) import base64 kdf.verify(bytes(password, "utf-8"), base64.urlsafe_b64decode(hashed_key)) except exceptions.InvalidKey: return False return True
print("\n\n") # ****************************************************************************************** # ****************************************************************************************** digest = mypass print("**** Testing with Scrypt from hazmat...") # ******** Time ********** start = time.time() for counter in range(iterations): print("Iteration %s from %s..." % (counter + 1, iterations)) # digest = scrypt_from_hashlib(password=digest, salt=mysalt, n=memory, r=8, p=1, maxmem=2147483646, dklen=128) backend = default_backend() kdf = scrypt_from_hazmat.Scrypt(salt=mysalt, length=128, n=memory, r=8, p=1, backend=backend) digest = kdf.derive(digest) # ******** Time ********** end = time.time() print("*** Time: ", end - start, "**********************************************") print("Digest in base64 format:", binascii.b2a_base64(digest).decode("utf-8")) print("\n\n") # ******************************************************************************************