import random from Crypto.Util.number import * from Crypto.Util.Padding import pad from flag import flag p = getStrongPrime(512) q = getStrongPrime(512) n = p * q B = getStrongPrime(512) m = flag[0:len(flag)//2] print("flag1_len =",len(m)) m1 = bytes_to_long(m) m2 = bytes_to_long(pad(m,128)) assert m1 < n assert m2 < n c1 = (m1*(m1+B)) % n c2 = (m2*(m2+B)) % n print("n =",n) print("B =",B) print("c1 =",c1) print("c2 =",c2) # Harder! m = flag[len(flag)//2:] print("flag2_len =",len(m)) m1 = bytes_to_long(m) m1 <<= ( (128-len(m))*8 ) m1 += random.SystemRandom().getrandbits( (128-len(m))*8 ) m2 = bytes_to_long(m) m2 <<= ( (128-len(m))*8 ) m2 += random.SystemRandom().getrandbits( (128-len(m))*8 ) assert m1 < n assert m2 < n c1 = (m1*(m1+B)) % n c2 = (m2*(m2+B)) % n print("hard_c1 =",c1) print("hard_c2 =",c2)
RabinCryptosystemが2つある
前半パートは
かつで、既知なので実質変数一つ
適当にFranklin-Reiter Related Message Attackをやって解く。
PR.<x> = PolynomialRing(Zmod(n)) l = 128 - 98 y = x * 2**(l*8) + int.from_bytes(bytes([l]) * l, "big") f1 = x*x + x*B - c1 f2 = y*y + y*B - c2 def pgcd(a, b): while b: a, b = b, a % b return a.monic() flag1 = bytes.fromhex(hex(-pgcd(f1, f2).coefficients()[0])[2:]) print(flag1)
後半パートは
で、で今度はは完全な乱数
しかしこれもと置くことで1変数 + 小さい誤差の式にできる。
resultantをつかって変数を式から追い出しながら、を求める方針で解ける
Coppersmith's Short Pad Attack
PR.<x, y> = PolynomialRing(Zmod(n)) PZ.<xz, yz> = PolynomialRing(Zmod(n)) f1 = x*(x + B) - hard_c1 f2 = (x+y)*(x + y + B) - hard_c2 q1 = f1.change_ring(PZ) q2 = f2.change_ring(PZ) PN.<xn> = PolynomialRing(Zmod(n)) f = q1.resultant(q2).univariate_polynomial().change_ring(PN).subs(y=xn) diffs = f.small_roots(X=2**(l*8), beta=1, epsilon=0.025) PR.<x> = PolynomialRing(Zmod(n)) for d in diffs: f1 = x*(x + B) - hard_c1 f2 = (x+d)*(x + d + B) - hard_c2 flag2 = bytes.fromhex(hex(-pgcd(f1, f2).coefficients()[0])[2:]) print(flag2)