#!/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