[61dctf]rsa
此题为Dual RSA
是找到一对ed符合两个余数
assert int(pow(pow(0xdeadbeef, e, n1), d)) == 0xdeadbeef assert int(pow(pow(0xdeadbeef, e, n2), d)) == 0xdeadbeef
下面是引用的exp
from sage.all import *
import math
import itertools
import alice_public_key
import bob_public_key
# display matrix picture with 0 and X
# references: https://github.com/mimoo/RSA-and-LLL-attacks/blob/master/boneh_durfee.sage
def matrix_overview(BB):
for ii in range(BB.dimensions()[0]):
a = ('%02d ' % ii)
for jj in range(BB.dimensions()[1]):
a += ' ' if BB[ii,jj] == 0 else 'X'
if BB.dimensions()[0] < 60:
a += ' '
print a
def dual_rsa_liqiang_et_al(e, n1, n2, delta, mm, tt):
'''
Attack to Dual RSA: Liqiang et al.'s attack implementation
References:
[1] Liqiang Peng, Lei Hu, Yao Lu, Jun Xu and Zhangjie Huang. 2016. "Cryptanalysis of Dual RSA"
'''
N = (n1+n2)/2
A = ZZ(floor(N^0.5))
_XX = ZZ(floor(N^delta))
_YY = ZZ(floor(N^0.5))
_ZZ = ZZ(floor(N^(delta - 1./4)))
_UU = _XX * _YY + 1
# Find a "good" basis satisfying d = a1 * l'11 + a2 * l'21
M = Matrix(ZZ, [[A, e], [0, n1]])
B = M.LLL()
l11, l12 = B[0]
l21, l22 = B[1]
l_11 = ZZ(l11 / A)
l_21 = ZZ(l21 / A)
modulo = e * l_21
F = Zmod(modulo)
PR = PolynomialRing(F, 'u, x, y, z')
u, x, y, z = PR.gens()
PK = PolynomialRing(ZZ, 'uk, xk, yk, zk')
uk, xk, yk, zk = PK.gens()
# For transform xy to u-1 (unravelled linearlization)
PQ = PK.quo(xk*yk+1-uk)
f = PK(x*(n2 + y) - e*l_11*z + 1)
fbar = PQ(f).lift()
# Polynomial construction
gijk = {}
for k in xrange(0, mm + 1):
for i in xrange(0, mm-k + 1):
for j in xrange(0, mm-k-i + 1):
gijk[i, j, k] = PQ(xk^i * zk^j * PK(fbar) ^ k * modulo^(mm-k)).lift()
hjkl = {}
for j in xrange(1, tt + 1):
for k in xrange(floor(mm / tt) * j, mm + 1):
for l in xrange(0, k + 1):
hjkl[j, k, l] = PQ(yk^j * zk^(k-l) * PK(fbar) ^ l * modulo^(mm-l)).lift()
monomials = []
for k in gijk.keys():
monomials += gijk[k].monomials()
for k in hjkl.keys():
monomials += hjkl[k].monomials()
monomials = sorted(set(monomials))[::-1]
assert len(monomials) == len(gijk) + len(hjkl) # square matrix?
dim = len(monomials)
# Create lattice from polynmial g_{ijk} and h_{jkl}
M = Matrix(ZZ, dim)
row = 0
for k in gijk.keys():
for i, monomial in enumerate(monomials):
M[row, i] = gijk[k].monomial_coefficient(monomial) * monomial.subs(uk=_UU, xk=_XX, yk=_YY, zk=_ZZ)
row += 1
for k in hjkl.keys():
for i, monomial in enumerate(monomials):
M[row, i] = hjkl[k].monomial_coefficient(monomial) * monomial.subs(uk=_UU, xk=_XX, yk=_YY, zk=_ZZ)
row += 1
matrix_overview(M)
print '=' * 128
# LLL
B = M.LLL()
matrix_overview(B)
# Construct polynomials from reduced lattices
H = [(i, 0) for i in xrange(dim)]
H = dict(H)
for j in xrange(dim):
for i in xrange(dim):
H[i] += PK((monomials[j] * B[i, j]) / monomials[j].subs(uk=_UU, xk=_XX, yk=_YY, zk=_ZZ))
H = H.values()
PQ = PolynomialRing(QQ, 'uq, xq, yq, zq')
uq, xq, yq, zq = PQ.gens()
# Inversion of unravelled linearlization
for i in xrange(dim):
H[i] = PQ(H[i].subs(uk=xk*yk+1))
# Calculate Groebner basis for solve system of equations
'''
Actually, These polynomials selection (H[1:20]) is heuristic selection.
Because they are "short" vectors. We need a short vector less than
Howgrave-Graham bound. So we trying test parameter(at [1]) and decided it.
'''
I = Ideal(*H[1:20])
g = I.groebner_basis('giac')[::-1]
mon = map(lambda t: t.monomials(), g)
PX = PolynomialRing(ZZ, 'xs')
xs = PX.gen()
x_pol = y_pol = z_pol = None
for i in xrange(len(g)):
if mon[i] == [xq, 1]:
print g[i] / g[i].lc()
x_pol = g[i] / g[i].lc()
elif mon[i] == [yq, 1]:
print g[i] / g[i].lc()
y_pol = g[i] / g[i].lc()
elif mon[i] == [zq, 1]:
print g[i] / g[i].lc()
z_pol = g[i] / g[i].lc()
if x_pol is None or y_pol is None or z_pol is None:
print '[-] Failed: we cannot get a solution...'
return
x0 = x_pol.subs(xq=xs).roots()[0][0]
y0 = y_pol.subs(yq=xs).roots()[0][0]
z0 = z_pol.subs(zq=xs).roots()[0][0]
# solution check
assert f(x0*y0+1, x0, y0, z0) % modulo == 0
a0 = z0
a1 = (x0 * (n2 + y0) + 1 - e*l_11*z0) / (e*l_21)
d = a0 * l_11 + a1 * l_21
return d
if __name__ == '__main__':
delta = 0.334
mm = 4
tt = 2
n1 = alice_public_key.N1
n2 = alice_public_key.N2
e = alice_public_key.e
d1 = dual_rsa_liqiang_et_al(e, n1, n2, delta, mm, tt)
print '[+] d for alice = %d' % d1
n1 = bob_public_key.N1
n2 = bob_public_key.N2
e = bob_public_key.e
d2 = dual_rsa_liqiang_et_al(e, n1, n2, delta, mm, tt)
print '[+] d for bob = %d' % d2
d = ZZ(open('send.bak', 'rb').read().encode('hex'), 16)
m = ZZ(Mod(ZZ(Mod(d, bob_public_key.N1)^d2), alice_public_key.N2)^alice_public_key.e)
m_ = hex(m)
if len(m_) % 2 == 1:
m_ = '0' + m_
print repr(m_.decode('hex'))

浙公网安备 33010602011771号