from Crypto.Util.number import getStrongPrime, getRandomRange N = 1024 def generateKey(): p = getStrongPrime(N) q = (p - 1) // 2 x = getRandomRange(2, q) g = 2 h = pow(g, x, p) pk = (p, q, g, h) sk = x return (pk, sk) def encrypt(m, pk): (p, q, g, h) = pk r = getRandomRange(2, q) c1 = pow(g, r, p) c2 = m * pow(h, r, p) % p return (c1, c2) def main(): with open("flag.txt") as f: flag = f.read().strip() pk, sk = generateKey() with open("publickey.txt", "w") as f: f.write(f"p = {pk[0]}\n") f.write(f"q = {pk[1]}\n") f.write(f"g = {pk[2]}\n") f.write(f"h = {pk[3]}\n") with open("ciphertext.txt", "w") as f: for m in flag: c = encrypt(ord(m), pk) f.write(f"{c}\n") if __name__ == "__main__": main()
ElGamal暗号だが、平文はなのでこれに注目する。
だがは実は合成数(pycryptodomeのgetStrongPrimeはでが大きな素数を約数に持つものを出力するらしい)。
というわけでの約数としてが得られる。なのでPohlig-Hellman Attack するとが得られる。
ある に対してを予想すればの組が得られる。が正しく予想できていればこの2値のpohligh hellmanからが得られることを利用できそうだが、実際にはうまくいかない(が手に入ってしまうことがある)
そこでこう考えてみる。
と置き、候補となるに対して対応するを求めておく。
のpohlig-hellmanから を得る。
は知っているので、としてに対応するが得られる
これで候補が絞れるのでいい感じにやるとオッケー
p = 168144747387516592781620466787069575171940752179672411574452734808497653671359884981272746489813635225263167370526619987842319278446075098036112998679570069486935297242638675590736039429506131690941660748942375274820626186241210376537247501823653926524570571499198040207829317830442983944747691656715907048411 q = 84072373693758296390810233393534787585970376089836205787226367404248826835679942490636373244906817612631583685263309993921159639223037549018056499339785034743467648621319337795368019714753065845470830374471187637410313093120605188268623750911826963262285285749599020103914658915221491972373845828357953524205 g = 2 h = 98640592922797107093071054876006959817165651265269454302952482363998333376245900760045606011965672215605936345612030149799453733708430421685495677502147392514542499678987737269487279698863617849581626352877756515435930907093553607392143564985566046429416461073375036461770604488387110385404233515192951025299 def pohlig_hellman(g, h, p, limit=100000): factors = [f[0] for f in (p-1).factor(limit=limit) if f[0] <= limit] bs = [] mods = [] for f in factors: k = (p-1)//f b = discrete_log(pow(h,k,p), pow(g,k,p), ord=f) bs.append(b) mods.append(f) return CRT(bs, mods) # assert # for _ in range(100): # x = randint(2, p-1) # x_ = pohlig_hellman(g, power_mod(g, x, p), p) # assert x % (2*3*5*19) == x_ table = {} for m in range(0x20, 0x7f): s = pohlig_hellman(g, m, p) if s in table: table[s] += chr(m) else: table[s] = chr(m) x = pohlig_hellman(g, h, p) lines = open("ciphertext.txt").read().strip().split("\n") flag = "" MOD = (2*3*5*19) for l in lines: c1, c2 = eval(l) r = pohlig_hellman(g, c1, p) s_rx = pohlig_hellman(g, c2, p) s = (s_rx - r*x) % MOD if s in table: if len(table[s]) == 1: flag += table[s] else: flag += "["+ table[s] + "]" print(flag)