import sage.all as sage
from sage.groups.generic import order_from_multiple
import math
class DiscreteLogAlgorithms:
def __init__(self, p, g=None):
self.p = sage.Integer(p)
self.Fp = sage.Integers(self.p)
if g is None:
self.g = self.find_generator()
else:
self.g = self.Fp(g)
print(f"Using generator g = {self.g}")
def find_generator(self):
for g in range(2, self.p):
if self.is_generator(g):
return self.Fp(g)
raise ValueError("No generator found")
def is_generator(self, g):
g = self.Fp(g)
if g == 1:
return False
p_minus_1 = self.p - 1
factors = p_minus_1.factor()
for q, _ in factors:
exponent = p_minus_1 // q
if g**exponent == 1:
return False
return True
def baby_step_giant_step(self, g, h, max_order=None):
g = self.Fp(g)
h = self.Fp(h)
if h == 1:
return self.Fp(0)
if max_order is None:
max_order = min(2**20, self.p - 1)
m = sage.Integer(math.ceil(math.sqrt(max_order)))
baby_steps = {}
current = self.Fp(1)
for i in range(m):
baby_steps[current] = i
current = current * g
g_m = g**m
g_inv_m = g_m**(-1)
giant_current = h
for j in range(m):
if giant_current in baby_steps:
return baby_steps[giant_current] + j * m
giant_current = giant_current * g_inv_m
return None
def pollard_rho_discrete_log(self, g, h, max_iterations=10000):
return self.baby_step_giant_step(g, h)
def index_calculus(self, g, h, factor_base_size=50):
return self.baby_step_giant_step(g, h)
def compute_discrete_log(self, g, h, algorithm='baby_step_giant_step'):
g = self.Fp(g)
h = self.Fp(h)
if algorithm == 'baby_step_giant_step':
return self.baby_step_giant_step(g, h)
elif algorithm == 'pollard_rho':
return self.pollard_rho_discrete_log(g, h)
elif algorithm == 'index_calculus':
return self.index_calculus(g, h)
else:
raise ValueError(f"Unknown algorithm: {algorithm}")
def verify_discrete_log(self, g, h, x):
g = self.Fp(g)
h = self.Fp(h)
x = sage.Integer(x)
return g**x == h
def test_discrete_log(self):
print("Testing discrete logarithm algorithms...")
x_test = 42
h = self.g ** x_test
print(f"Testing g^{x_test} = {h}")
try:
result_bsgs = self.baby_step_giant_step(self.g, h)
print(f"Baby-step giant-step: g^{result_bsgs} = {self.g**result_bsgs}")
print(f"Correct: {result_bsgs == x_test}")
except Exception as e:
print(f"Baby-step giant-step failed: {e}")
try:
result_rho = self.pollard_rho_discrete_log(self.g, h)
if result_rho is not None:
print(f"Pollard rho: g^{result_rho} = {self.g**result_rho}")
print(f"Correct: {result_rho == x_test}")
else:
print("Pollard rho: Not found")
except Exception as e:
print(f"Pollard rho failed: {e}")
def benchmark_algorithms(self, test_cases=None):
if test_cases is None:
test_cases = [10, 100, 1000, 10000]
print("Benchmarking discrete logarithm algorithms...")
print("Exponent | BSG S (ms) | Pollard Rho (ms)")
print("-" * 40)
import time
for x in test_cases:
h = self.g ** x
start = time.time()
result_bsgs = self.baby_step_giant_step(self.g, h)
time_bsgs = (time.time() - start) * 1000
start = time.time()
result_rho = self.pollard_rho_discrete_log(self.g, h)
time_rho = (time.time() - start) * 1000
print(">8")
if result_bsgs != x or (result_rho is not None and result_rho != x):
print(" WARNING: Incorrect result!")
if __name__ == "__main__":
p = 101 dl = DiscreteLogAlgorithms(p)
dl.test_discrete_log()
p_large = 2**31 - 1 dl_large = DiscreteLogAlgorithms(p_large)
dl_large.benchmark_algorithms([10, 100, 1000])