def index_calculus(g, h, p, b=0): if not b: b = ceil(exp(sqrt(log(p)*(log(log(p)))))**(1/sqrt(2))) primes = nt.eratosthenes(b) crt_factors = nt.prime_factorise(p-1, counted=True) relations = [] # we need to find a number of linearly dependent relations # first we'll just find 4 times as many as we need while len(relations) < 4*len(primes): x = randint(1, p-1) y = pow(g, x, p) if nt.is_b_smooth(y, b): factorisation = nt.prime_factorise(y, counted=True) relation = [factorisation.get(q, 0) for q in primes] + [x] relations.append(relation) # perform Gaussian elimination on relations modulo each factor sols = [] for base in sorted(crt_factors.keys()): m = base**crt_factors[base] sol = nt.reduced_row_echelon(relations, m) sols.append([sol[i][-1] for i in range(len(sol[0])-1)]) small_prime_logs = [] # use the CRT to stitch together the solutions for i in range(len(primes)): congruences = [] for j, base in enumerate(sorted(crt_factors.keys())): m = base**crt_factors[base] congruences.append((sols[j][i], m)) small_prime_logs.append(nt.chinese_remainder_theorem(congruences)[0]) # have the logs of the small primes g_inv = nt.mod_mult_inv(g, p) g_k = g_inv for k in range(1, p): v = (h * g_k) % p if nt.is_b_smooth(v, b): out = k factors = nt.prime_factorise(v, counted=True) for i, q in enumerate(primes): out += small_prime_logs[i] * factors[q] return out % (p-1) g_k *= g_inv
def mult_inverse(self): return IntModP(nt.mod_mult_inv(self.value, self.p), self.p)