Initial commit
This commit is contained in:
@@ -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)
|
||||
Reference in New Issue
Block a user