Files
basicswap/basicswap/util/rfc2440.py
2025-04-10 10:32:42 -04:00

80 lines
2.1 KiB
Python

# -*- coding: utf-8 -*-
import hashlib
import secrets
def rfc2440_hash_password(password, salt=None):
# Match tor --hash-password
# secret_to_key_rfc2440
EXPBIAS = 6
c = 96
count = (16 + (c & 15)) << ((c >> 4) + EXPBIAS)
if salt is None:
salt = secrets.token_bytes(8)
assert len(salt) == 8
hashbytes = salt + password.encode("utf-8")
len_hashbytes = len(hashbytes)
h = hashlib.sha1()
while count > 0:
if count >= len_hashbytes:
h.update(hashbytes)
count -= len_hashbytes
continue
h.update(hashbytes[:count])
break
rv = "16:" + salt.hex() + "60" + h.hexdigest()
return rv.upper()
def verify_rfc2440_password(stored_hash, provided_password):
"""
Verifies a password against a hash generated by rfc2440_hash_password.
Args:
stored_hash (str): The hash string stored (e.g., "16:<salt>60<hash>").
provided_password (str): The password attempt to verify.
Returns:
bool: True if the password matches the hash, False otherwise.
"""
try:
parts = stored_hash.upper().split(":")
if len(parts) != 2 or parts[0] != "16":
return False
salt_hex_plus_hash_hex = parts[1]
separator_index = salt_hex_plus_hash_hex.find("60")
if separator_index != 16:
return False
salt_hex = salt_hex_plus_hash_hex[:separator_index]
expected_hash_hex = salt_hex_plus_hash_hex[separator_index + 2 :]
salt = bytes.fromhex(salt_hex)
except (ValueError, IndexError):
return False
EXPBIAS = 6
c = 96
count = (16 + (c & 15)) << ((c >> 4) + EXPBIAS)
hashbytes = salt + provided_password.encode("utf-8")
len_hashbytes = len(hashbytes)
h = hashlib.sha1()
while count > 0:
if count >= len_hashbytes:
h.update(hashbytes)
count -= len_hashbytes
continue
h.update(hashbytes[:count])
break
calculated_hash_hex = h.hexdigest().upper()
return secrets.compare_digest(calculated_hash_hex, expected_hash_hex)