108 lines
2.9 KiB
Python
108 lines
2.9 KiB
Python
#!/usr/bin/env python3.9
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
JFinal HashKit - Hash and Encryption Utilities
|
|
"""
|
|
|
|
import hashlib
|
|
import secrets
|
|
import threading
|
|
|
|
class HashKit:
|
|
"""Hash utility class"""
|
|
|
|
FNV_OFFSET_BASIS_64 = 0xcbf29ce484222325
|
|
FNV_PRIME_64 = 0x100000001b3
|
|
|
|
_HEX_DIGITS = "0123456789abcdef"
|
|
_CHAR_ARRAY = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
|
@staticmethod
|
|
def fnv1a64(key: str) -> int:
|
|
"""FNV-1a 64-bit hash"""
|
|
hash_val = HashKit.FNV_OFFSET_BASIS_64
|
|
for i in range(len(key)):
|
|
hash_val ^= ord(key[i])
|
|
hash_val *= HashKit.FNV_PRIME_64
|
|
return hash_val
|
|
|
|
@staticmethod
|
|
def md5(src_str: str) -> str:
|
|
"""MD5 hash"""
|
|
return HashKit._hash("md5", src_str)
|
|
|
|
@staticmethod
|
|
def sha1(src_str: str) -> str:
|
|
"""SHA-1 hash"""
|
|
return HashKit._hash("sha1", src_str)
|
|
|
|
@staticmethod
|
|
def sha256(src_str: str) -> str:
|
|
"""SHA-256 hash"""
|
|
return HashKit._hash("sha256", src_str)
|
|
|
|
@staticmethod
|
|
def sha384(src_str: str) -> str:
|
|
"""SHA-384 hash"""
|
|
return HashKit._hash("sha384", src_str)
|
|
|
|
@staticmethod
|
|
def sha512(src_str: str) -> str:
|
|
"""SHA-512 hash"""
|
|
return HashKit._hash("sha512", src_str)
|
|
|
|
@staticmethod
|
|
def _hash(algorithm: str, src_str: str) -> str:
|
|
"""Generic hash function"""
|
|
try:
|
|
md = hashlib.new(algorithm)
|
|
md.update(src_str.encode('utf-8'))
|
|
return HashKit.to_hex(md.digest())
|
|
except Exception as e:
|
|
raise RuntimeError(e)
|
|
|
|
@staticmethod
|
|
def to_hex(bytes_data: bytes) -> str:
|
|
"""Convert bytes to hex string"""
|
|
ret = []
|
|
for b in bytes_data:
|
|
ret.append(HashKit._HEX_DIGITS[(b >> 4) & 0x0f])
|
|
ret.append(HashKit._HEX_DIGITS[b & 0x0f])
|
|
return ''.join(ret)
|
|
|
|
@staticmethod
|
|
def generate_salt(salt_length: int) -> str:
|
|
"""Generate random salt"""
|
|
salt = []
|
|
for i in range(salt_length):
|
|
salt.append(secrets.choice(HashKit._CHAR_ARRAY))
|
|
return ''.join(salt)
|
|
|
|
@staticmethod
|
|
def generate_salt_for_sha256() -> str:
|
|
"""Generate salt for SHA-256"""
|
|
return HashKit.generate_salt(32)
|
|
|
|
@staticmethod
|
|
def generate_salt_for_sha512() -> str:
|
|
"""Generate salt for SHA-512"""
|
|
return HashKit.generate_salt(64)
|
|
|
|
@staticmethod
|
|
def generate_random_str(str_length: int) -> str:
|
|
"""Generate random string"""
|
|
return HashKit.generate_salt(str_length)
|
|
|
|
@staticmethod
|
|
def slow_equals(a: bytes, b: bytes) -> bool:
|
|
"""Timing-safe byte comparison"""
|
|
if a is None or b is None:
|
|
return False
|
|
if len(a) != len(b):
|
|
return False
|
|
|
|
diff = 0
|
|
for i in range(len(a)):
|
|
diff |= a[i] ^ b[i]
|
|
return diff == 0
|