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,88 @@
import random
import math
class PrimeNumber:
"""
Класс-обёртка для простого числа.
Контракт: внутри всегда хранится валидное простое число.
"""
MAX_ATTEMPTS: int = 1000 # лимит попыток генерации
def __init__(self, value: int):
if not self._is_prime(value):
raise ValueError(f"Число {value} не является простым")
self._value = value
@classmethod
def from_value(cls, value: int) -> "PrimeNumber":
"""Фабричный метод: создать простое число из заданного значения."""
return cls(value)
@classmethod
def random(
cls,
min_value: int | None = None,
max_value: int | None = None,
seed: int | None = None
) -> "PrimeNumber":
"""
Случайное простое число из диапазона [min_value, max_value].
Если seed указан, ГСЧ инициализируется для воспроизводимости.
"""
if min_value < 2:
raise ValueError("Нижняя граница min_value должна быть ≥ 2")
if min_value > max_value:
raise ValueError("min_value не может быть больше max_value")
if seed is not None:
random.seed(seed)
for _ in range(cls.MAX_ATTEMPTS):
candidate = random.randint(min_value, max_value)
if cls._is_prime(candidate):
return cls(candidate)
raise ValueError(
f"Не удалось найти простое число в диапазоне [{
min_value}, {max_value}] "
f"за {cls.MAX_ATTEMPTS} попыток"
)
@classmethod
def random_bits(cls, bits: int, seed: int | None = None) -> "PrimeNumber":
if bits < 2:
raise ValueError("Количество бит должно быть не меньше 2")
# Нижняя граница — 2^(bits-1), верхняя — 2^bits - 1
min_value = 1 << (bits - 1)
max_value = (1 << bits) - 1
return cls.random(
min_value=min_value,
max_value=max_value,
seed=seed,
)
@property
def value(self) -> int:
return self._value
def __repr__(self) -> str:
return f"PrimeNumber({self._value})"
@staticmethod
def _is_prime(n: int) -> bool:
"""Проверка на простоту (НУ ОООЧЕНЬ МЕДЛЕННАЯ)."""
if n < 2:
return False
if n in (2, 3):
return True
if n % 2 == 0:
return False
limit = int(math.isqrt(n)) + 1
for i in range(3, limit, 2):
if n % i == 0:
return False
return True
@@ -0,0 +1,51 @@
from math import gcd
from .prime_number import PrimeNumber
from .public_key import PublicKey
class PrivateKey:
"""
Закрытый ключ RSA.
p1, p2 — экземпляры PrimeNumber.
e — экземпляр PrimeNumber (открытая степень).
n = p1 * p2 (int).
"""
def __init__(self, p1: PrimeNumber, p2: PrimeNumber):
if not isinstance(p1, PrimeNumber) or not isinstance(p2, PrimeNumber):
raise TypeError("p1 и p2 должны быть экземплярами PrimeNumber")
if p1.value == p2.value:
raise ValueError("p1 и p2 должны быть разными простыми")
self.p1 = p1
self.p2 = p2
self.n = p1.value * p2.value
self.phi = (p1.value - 1) * (p2.value - 1)
self.e = self._choose_e()
self.d = self._compute_d()
def _choose_e(self) -> PrimeNumber:
# ищем простое e, взаимнопростое с φ(n)
candidate = 3
while candidate < self.phi:
if gcd(candidate, self.phi) == 1 and PrimeNumber._is_prime(candidate):
return PrimeNumber(candidate)
candidate += 2
raise ValueError("Не удалось подобрать e")
def _compute_d(self) -> int:
k = 1
while True:
if (k * self.phi + 1) % self.e.value == 0:
return (k * self.phi + 1) // self.e.value
k += 1
def make_public_key(self) -> PublicKey:
return PublicKey(self.e, self.n)
def decrypt(self, c: int) -> int:
if not isinstance(c, int) or c < 0:
raise ValueError(
"Шифртекст c должен быть неотрицательным целым числом")
return pow(c, self.d, self.n)
@@ -0,0 +1,23 @@
from .prime_number import PrimeNumber
class PublicKey:
"""
Публичный ключ (e, n).
e — экземпляр PrimeNumber (простое число).
n — целое (составное число p1*p2).
"""
def __init__(self, e: PrimeNumber, n: int):
if not isinstance(e, PrimeNumber):
raise TypeError("e должно быть экземпляром PrimeNumber")
if not isinstance(n, int):
raise TypeError("n должно быть int (составное число)")
self.e = e
self.n = n
def encrypt(self, m: int) -> int:
if not isinstance(m, int) or m < 0:
raise ValueError(
"Сообщение m должно быть неотрицательным целым числом")
return pow(m, self.e.value, self.n)