from Crypto.Util.number import bytes_to_long, getStrongPrime from random import randrange from secret import flag LIMIT = 64 def gen(): p = getStrongPrime(512) g = randrange(1, p) return g, p def main(): g, p = gen() print("g:", str(g)) print("p:", str(p)) x = bytes_to_long(flag) enc = pow(g, x, p) print("encrypted flag:", str(enc)) ctr = 0 while ctr < LIMIT: try: div = int(input("give me a number> ")) print(pow(g, x // div, p)) ctr += 1 except: print("whoops..") return print("no more tries left... bye") main()
というdiscrete logをときたい。を入力としてあたえるとを計算してくれるオラクルが64回実行できる
一見するとをとしてうまくlegendre記号などを考えることで1bitずつリークできそうに見える。しかしオラクルは64回しか実行できない。
だがさらによくみるとフラグは毎回固定なので64回のオラクルを何回も実行でき、1bitずつリークできるならフラグ全体が読めることがわかる
オラクルについて考えてみる。がにおいて平方剰余でないとき、がquadratic residueかどうかをみることでの偶奇がわかる。とけそう
別解
getStrongPrimeはsafe primeを返すわけではないことを思い出すと、小さい素因数を集めてきてPohlig-Hellman Attackによってdiscrete logが解ける。512bitを復元するに足るまでmodを集めることでオラクルを一切使わずに問題が解ける
別々解
のとき、となることをオラクルとして二分探索が可能。