Path: blob/master/Public Key/Diffie Hellman/pohlig_hellman_EC.sage
336 views
# Sage already comes built-in with a discrete logarithm function that uses a Pohlig-Hellman / BSGS backend.1# However, it can never hurt to implement the algorithm yourself for sake of understanding.234def generate_params(B=2^10, num_factors=8):5""" Generates the public and private parameters for Diffie-Hellman """67# Generates num_factors primes and multiplies them together to form a modulus8p = 1910# Apparently it's possible to generate curves of desired order using complex multiplication,11# but that's out of scope for this script. Instead, we'll set a bound on the private keys based on $B$.1213while not is_prime(p):14p = 2 * prod([random_prime(B, proof=False) for i in range(num_factors)]) + 11516F = GF(p)1718a4 = randint(0, num_factors * B)19a6 = randint(0, num_factors * B)20kA = randint(0, p)21kB = randint(0, p)2223E = EllipticCurve(F, [a4, a6])24G = E.gens()[0]2526PA = kA * G27PB = kB * G2829assert PA * kB == PB * kA30return G, PA, E3132# The Baby-Step Giant Step (BSGS) algorithm helps reduce the complexity of calculating the discrete logarithm33# g_i^x_i mod p_i = h_i to O(sqrt(p_i)) instead of O(p_i) with traditional brute force. The way BSGS works is that34# We rewrite the discrete logarithm x_i in terms of im + j, where m = ceil(sqrt(n)). This allows for a meet-in-the-middle35# style calculation of $x$--namely, we first calculate g^j mod p for every 0 <= j < m, and then calculate g^i mod p for36# 0 <= j <= p, multiplying by a^-m for every y not equal to3738def BSGS(G, PA, n, E):3940# Normally ceil(sqrt(n)) should work but for some reason some test cases break this41M = ceil(sqrt(n)) + 142y = PA43log_table = {}4445for j in range(M):46log_table[j] = (j, j * G)4748inv = -M * G4950for i in range(M):51for x in log_table:52if log_table[x][1] == y:53return i * M + log_table[x][0]5455y += inv5657return None5859# The Pohlig-Hellman attack on Diffie-Hellman works as such:60# Given the generator, public keys of either Alice or Bob, as well as the multiplicative order61# Of the group (which in Diffie-Hellman is p - 1 due to prime modulus),62# one can factor the group order (which by construction here is B-smooth) into63# Small primes. By Lagrange's theorem, we have that646566def pohlig_hellman_EC(G, PA, E, debug=True):67""" Attempts to use Pohlig-Hellman to compute discrete logarithm of A = g^a mod p"""6869# This code is pretty clunky, naive, and unoptimized at the moment, but it works.7071n = E.order()72factors = [p_i ^ e_i for (p_i, e_i) in factor(n)]73crt_array = []7475if debug:76print("[x] Factored #E(F_p) into %s" % factors)7778for p_i in factors:79g_i = G * (n // p_i)80h_i = PA * (n // p_i)81x_i = BSGS(g_i, h_i, p_i, E)82if debug and x_i != None:83print("[x] Found discrete logarithm %d for factor %d" % (x_i, p_i))84crt_array += [x_i]8586elif x_i == None:87print("[] Did not find discrete logarithm for factor %d" % p_i)888990return crt(crt_array, factors)9192def test():9394# Not sure why B is here, it isn't even used95print("Generating parameters...")96G, PA, E = generate_params()97print("Attempting Pohlig-Hellman factorization with \nG = %s\nPA = %s\nE is an %s\n"98% (G, PA, E))99kA = pohlig_hellman_EC(G, PA, E)100assert kA * G == PA101print("[x] Recovered scalar kA such that PA = G * kA through Pohlig-Hellman: %d" % kA)102103if __name__ == "__main__":104test()105106