david22guy's picture
Upload folder using huggingface_hub
8d1e50f verified
Raw
History Blame Contribute Delete
3.23 kB
from __future__ import annotations
from dataclasses import dataclass
import logging
from typing import *
import gmpy2
from . import Challenge, Serde
_logger = logging.getLogger(__name__)
@dataclass
class Problem(Serde):
difficulty: int
num: int
num_bits: int
@dataclass
class Solution(Serde):
status: str
p: Optional[int]
q: Optional[int]
@dataclass
class Verif(Serde):
n: int
p: int
q: int
def validate_breaking_rsa_solution(solution: Solution, verif: Verif, prob: Problem | None=None, require_success_status: bool=True) -> tuple[bool, str | None]:
if require_success_status and getattr(solution, 'status', None) != 'success':
return (False, f"Solution status is '{getattr(solution, 'status', None)}', not 'success'")
if solution.p is None or solution.q is None:
return (False, 'Solution has missing factors (p or q is null)')
try:
sol_p = int(solution.p)
sol_q = int(solution.q)
except (TypeError, ValueError):
return (False, f'Solution p/q are not valid integers: p={solution.p}, q={solution.q}')
sol_p, sol_q = (min(sol_p, sol_q), max(sol_p, sol_q))
p_check, q_check = (min(verif.p, verif.q), max(verif.p, verif.q))
n = verif.n
if sol_p * sol_q != n:
return (False, f'p * q != n: {sol_p} * {sol_q} != {n}')
if sol_p != p_check or sol_q != q_check:
return (False, f"Factors don't match expected: got ({sol_p}, {sol_q}), expected ({p_check}, {q_check})")
if prob is not None:
if prob.num != n or sol_p * sol_q != prob.num:
return (False, f'n mismatch between problem ({prob.num}) and verif ({n})')
return (True, None)
def _gen_prime(num_bits: int, rng: gmpy2.random_state) -> gmpy2.mpz:
n = gmpy2.mpz_urandomb(rng, num_bits)
n |= 1 << num_bits - 1 | 1
return gmpy2.next_prime(n)
@dataclass
class BreakingRSA(Challenge[Problem, Solution, Verif]):
difficulty: int
num_bits: int
def generate(self, seed: int) -> tuple[Problem, Verif]:
rng = gmpy2.random_state(seed)
bits_p = self.num_bits // 2
bits_q = self.num_bits - bits_p
fermat_thresh = 2 ** max(bits_p - 100, 1)
while True:
p = _gen_prime(bits_p, rng)
q = _gen_prime(bits_q, rng)
n = p * q
if n.bit_length() == self.num_bits and abs(p - q) > fermat_thresh:
break
n = int(n)
p = int(p)
q = int(q)
_logger.info(f'Generated {self.num_bits}-bit semiprime ({len(str(n))} digits)')
problem = Problem(self.difficulty, n, self.num_bits)
verif = Verif(n, p, q)
return (problem, verif)
def verify(self, prob: Problem, sol: Solution, verif: Verif) -> bool:
success, reason = validate_breaking_rsa_solution(sol, verif, prob, require_success_status=False)
if success:
_logger.info('Verification SUCCESS')
elif sol.p is None or sol.q is None:
_logger.info(f'Verification FAILURE: missing factors, got p={sol.p} and' + f' q={sol.q}, with solution status {sol.status}')
else:
_logger.info(f'Verification FAILURE: {reason or 'factors do not match'}')
return success