Initial commit

This commit is contained in:
user
2026-07-12 14:06:45 +04:00
commit a75028d68f
148 changed files with 3275 additions and 0 deletions
@@ -0,0 +1,44 @@
import string
import random
from modes.hash_mode import HashMode
class UniformityAnalyzer:
"""
Анализ равномерности распределения хэшей.
"""
def run(self, variant: str, count: int = 100, buckets: int = 10, bytes: int = 16) -> dict:
hasher = HashMode()
alphabet = string.ascii_lowercase + string.digits
# случайные сообщения
messages = [
"".join(random.choice(alphabet)
for _ in range(random.randint(5, 10)))
for _ in range(count)
]
# хэши
hashes = [hasher.run(variant, msg, bytes=bytes) for msg in messages]
# берём первые 8 hex-символов → число
values = [int(h[:8], 16) for h in hashes]
values.sort()
# диапазоны
min_val, max_val = values[0], values[-1]
step = (max_val - min_val) / buckets if max_val > min_val else 1
distribution = [0] * buckets
for v in values:
idx = min(int((v - min_val) / step), buckets - 1)
distribution[idx] += 1
return {
"count": count,
"buckets": buckets,
"messages": messages,
"hashes": hashes,
"distribution": distribution,
}
+95
View File
@@ -0,0 +1,95 @@
# modes/hash_mode.py
import math
class HashMode:
"""
Учебная хэш-функция.
Варианты:
1 = sin-based
2 = cos-based
3 = xor-based
"""
def run(self, variant: str, message: str, salt: str = "", bytes: int = 16) -> str:
if not (2 <= bytes <= 16):
raise ValueError("Параметр bytes должен быть в диапазоне [2, 16]")
data = (message + salt).encode("utf-8")
if variant == "1":
out = self._sin_hash(data)
elif variant == "2":
out = self._cos_hash(data)
elif variant == "3":
out = self._xor_hash(data)
else:
raise ValueError(f"Неизвестный вариант: {variant}")
# Обрезаем до нужного числа байт
truncated = self._truncate_bytes(out, bytes)
return ''.join(f"{x:02x}" for x in truncated)
# --- служебные ------------------------------------------------------
def _truncate_bytes(self, out: bytearray, bytes: int) -> bytearray:
if bytes >= len(out):
return out
truncated = out[:bytes]
# примешиваем все отброшенные байты в оставшиеся
for i, b in enumerate(out[bytes:], start=1):
truncated[i % len(truncated)] ^= b
return truncated
# --- реализации ------------------------------------------------------
def _sin_hash(self, data: bytes) -> bytearray:
out = [0x55 ^ i for i in range(16)]
if not data:
return out
angle = (2 * math.pi) / (len(out) * len(data))
for i, b in enumerate(data, 1):
for j in range(len(out)):
val = int((math.sin(b + (i + j) * angle) + 1) * 127)
out[j] ^= val & 0xFF
return out
def _cos_hash(self, data: bytes) -> bytearray:
out = [0x77 ^ i for i in range(16)]
if not data:
return out
angle = (2 * math.pi) / (len(out) * len(data))
for i, b in enumerate(data, 1):
for j in range(len(out)):
val = int((math.cos(b + (i + j) * angle) + 1) * 127)
out[j] ^= val & 0xFF
return out
def _mix_bits(self, mix: int) -> bytearray:
# количество байт у числа = (битовая длина + 7) // 8
count = (mix.bit_length() + 7) // 8
for i in range(1, count):
mix ^= (mix >> (8 * i))
return mix
def _xor_hash(self, data: bytes) -> bytearray:
out = [0xAA ^ i for i in range(16)]
if not data:
return out
phi = (1 + 5 ** 0.5) / 2 # золотое сечение
for i, b in enumerate(data, 1):
for j in range(16):
mix = (b * (i + j + 1)) ^ int(phi * 1e6)
mix = self._mix_bits(mix)
out[j] ^= mix & 0xFF # только младший байт
return out
@@ -0,0 +1,39 @@
from modes.hash_mode import HashMode
class AvalancheTest:
"""
Проверка эффекта лавины:
небольшое изменение во входном сообщении должно сильно менять хэш.
"""
def run(self, variant: str, message: str, salt: str = "", bytes: int = 16) -> dict:
hasher = HashMode()
# базовый хэш
h1 = hasher.run(variant, message, salt=salt, bytes=bytes)
# изменяем один символ (если пусто — подставим "a")
if message:
i = 0 # всегда первый символ для простоты
flipped_char = chr((ord(message[i]) + 1) % 128)
mod_msg = flipped_char + message[1:]
else:
mod_msg = "a"
h2 = hasher.run(variant, mod_msg, salt=salt, bytes=bytes)
# сравнение побитово
diff_bits = sum(c1 != c2 for c1, c2 in zip(
bin(int(h1, 16))[2:], bin(int(h2, 16))[2:]))
total_bits = len(h1) * 4 # HEX -> биты
return {
"original": message,
"modified": mod_msg,
"hash1": h1,
"hash2": h2,
"diff_bits": diff_bits,
"total_bits": total_bits,
"diff_percent": round(100 * diff_bits / total_bits, 2),
}
@@ -0,0 +1,38 @@
import itertools
import string
from modes.hash_mode import HashMode
class CollisionsTest:
"""
Грубый поиск коллизий: перебираем строки до max_len
из заданного алфавита и проверяем, совпадут ли хэши.
"""
def run(self, variant: str, max_len: int = 3, alphabet: str = "alnum", bytes: int = 16) -> dict:
hasher = HashMode()
seen = {}
# выбор алфавита
if alphabet == "alnum":
chars = string.ascii_lowercase + string.digits
elif alphabet == "alpha":
chars = string.ascii_lowercase
elif alphabet == "digits":
chars = string.digits
else:
raise ValueError(f"Неизвестный алфавит: {alphabet}")
for length in range(1, max_len + 1):
for msg_tuple in itertools.product(chars, repeat=length):
msg = "".join(msg_tuple)
h = hasher.run(variant, msg, bytes=bytes)
if h in seen:
return {
"collision_found": True,
"hash": h,
"messages": [seen[h], msg],
}
seen[h] = msg
return {"collision_found": False}