# sagemath defAMM(o, r, q): print('\n------------------------------------------------------------------') print('Start to run Adleman-Manders-Miller Root Extraction Method') print('Try to find one {:#x}th root of {} modulo {}'.format(r, o, q)) g = GF(q) o = g(o) p = g(random.randint(1, q)) while p ^ ((q-1) // r) == 1: p = g(random.randint(1, q)) print('[+] Find p:{}'.format(p)) t = 0 s = q - 1 while s % r == 0: t += 1 s = s // r print('[+] Find s:{}, t:{}'.format(s, t)) k = 1 while (k * s + 1) % r != 0: k += 1 alp = (k * s + 1) // r print('[+] Find alp:{}'.format(alp)) a = p ^ (r**(t-1) * s) b = o ^ (r*alp - 1) c = p ^ s h = 1 for i inrange(1, t): d = b ^ (r^(t-1-i)) if d == 1: j = 0 else: print('[+] Calculating DLP...') j = - discrete_log(d, a) print('[+] Finish DLP...') b = b * (c^r)^j h = h * c^j c = c^r result = o^alp * h print('Find one solution: {}'.format(result)) return result
defget_full_p(p_low, n,d_low): PR.<x> = PolynomialRing(Zmod(n)) d_lowbits = d_low.nbits() nbits = n.nbits() p_lowbits = p_low.nbits() f = 2^p_lowbits*x + p_low f = f.monic() roots = f.small_roots(X=2^(nbits//2-p_lowbits), beta=0.4) if roots: x0 = roots[0] p = gcd(2^d_lowbits*x0 + p_low, n) return ZZ(p)
deffind_p_low(d_low, e, n): X = var('X') for k inrange(1, e+1): results = solve_mod([e*d_low*X == k*n*X + k*X + X-k*X**2 - k*n], 2^d_low.nbits()) for x in results: p_low = ZZ(x[0]) p = get_full_p(p_low, n,d_low) if p and p != 1: return p
n = 928965...... c = 5616437818...... d_low = 787673996...... e = 3 find_p_low(d_low, e, n)
from tqdm import * from Crypto.Util.number import * defget_full_p(p_high, n,d_high,bits): PR.<x> = PolynomialRing(Zmod(n)) f = x + p_high f = f.monic() roots = f.small_roots(X=2^(bits + 4), beta=0.4) if roots: x0 = roots[0] p = gcd(x0 + p_high, n) return ZZ(p)
deffind_p_high(d_high, e, n,bits): PR.<X> = PolynomialRing(RealField(1000)) for k in tqdm(range(1, e+1)): # 由于我们不知道d_low,我们近似认为 d ≈ d_high * 2^bits: f=e * d_high * X - (k*n*X + k*X + X-k*X**2 - k*n) results = f.roots() if results: for x in results: p_high = int(x[0]) p = get_full_p(p_high, n,d_high,bits) if p and p != 1: return p
idx = [(k - i, i) for k inrange(kk + 1) for i inrange(k + 1)] monomials = list(map(lambda t: PR(x ** t[0] * y ** t[1]), idx)) # collect the shift-polynomials g = [] for h, i in idx: if h == 0: g.append(y ** h * x ** i * N) else: g.append(y ** (h - 1) * x ** i * f)
# construct lattice basis M = Matrix(ZZ, len(g)) for row inrange(M.nrows()): for col inrange(M.ncols()): h, i = idx[col] M[row, col] = g[row][h, i] * XX ** h * YY ** i
# Transform LLL-reduced vectors to polynomials H = [(i, PR(0)) for i inrange(B.nrows())] H = dict(H) for i inrange(B.nrows()): for j inrange(B.ncols()): H[i] += PR((monomials[j] * B[i, j]) / monomials[j](XX, YY))
defsmall_roots(f, bounds, m=1, d=None): ifnot d: d = f.degree()
R = f.base_ring() N = R.cardinality()
f /= f.coefficients().pop(0) f = f.change_ring(ZZ)
G = Sequence([], f.parent()) for i inrange(m + 1): base = N ^ (m - i) * f ^ i for shifts in itertools.product(range(d), repeat=f.nvariables()): g = base * prod(map(power, f.variables(), shifts)) G.append(g)
factors = [monomial(*bounds) for monomial in monomials] for i, factor inenumerate(factors): B.rescale_col(i, factor)
B = B.dense_matrix().LLL()
B = B.change_ring(QQ) for i, factor inenumerate(factors): B.rescale_col(i, 1 / factor)
H = Sequence([], f.parent().change_ring(QQ)) for h infilter(None, B * monomials): H.append(h) I = H.ideal() if I.dimension() == -1: H.pop() elif I.dimension() == 0: roots = [] for root in I.variety(ring=ZZ): root = tuple(R(root[var]) for var in f.variables()) roots.append(root) return roots
defencrypt(pubkey, m): N, e = pubkey c = pow(m, e, N) return c
m = bytes_to_long(flag) d = getPrime(300) pubkeys = [get_public_key(d), get_public_key(d)] cs = encrypt(pubkeys[0], m), encrypt(pubkeys[1], m) print(pubkeys) print(cs)
e = 19999999999999999999999999999999999999999999999999999999999999999999999999997 n = 7195 dp = 34961 c = 3014 # edp = 1 + kp - k # edp + k - 1 = 0modp
PR.<x> = PolynomialRing(Zmod(n)) f = e * dp -1 + x f = f.monic() k = f.small_roots(X=e,beta=0.4)[0] print(k) assert (int(e)*int(dp) + int(k) -1) % int(k) == 0
p = ZZ((e*dp+k-1)/k) print(p) q = n / p L = (p-1)*(q-1) d = Integer(e).inverse_mod(L) m = Integer(pow(c, d, n)) flag = bytes.fromhex(m.hex()) print(flag.decode())
广义解法: m自选 $$ \begin{aligned} m \equiv c_m^{d_p} \mod p \ c_m^{d_p}-m=kp \ p=gcd(c_m^{d_p}-m,n) \end{aligned} $$
1 2 3 4 5 6 7
n = 71955 dp = 34961
m = 1997 c = pow(m, e, n) p = gcd(pow(c, dp, n) - m, n) assert n % p == 0
1.8.选择明密文攻击
注:这个方面的内容均来自CTFwiki
1.8.1. 选择明文攻击
**每次乘2,根据modN后是奇数还是偶数来判断 第 i 次时,xN/2^i ≤ P < xN+N/2^i
from Crypto.Util.number import long_to_bytes import itertools
defsmall_roots(f, bounds, m=1, d=None): ifnot d: d = f.degree()
R = f.base_ring() N = R.cardinality() f /= f.coefficients().pop(0) f = f.change_ring(ZZ)
G = [] for i inrange(m + 1): base = (N ** (m - i)) * (f ** i) for shifts in itertools.product(range(d), repeat=f.nvariables()): g = base for var, s inzip(f.variables(), shifts): g *= var ** s G.append(g)
# 收集所有单项式 monomials = set() for g in G: monomials |= set(g.monomials()) monomials = sorted(monomials)
# 构造矩阵 B rows = [] for g in G: rows.append([g.monomial_coefficient(m) for m in monomials]) B = matrix(ZZ, rows)
# 缩放 factors = [] for m in monomials: fac = 1 for var, b inzip(f.variables(), bounds): fac *= b ** m.degree(var) factors.append(fac)
B = B.change_ring(QQ) for col, fac inenumerate(factors): B.rescale_col(col, fac)
# LLL B = B.dense_matrix().LLL()
# 还原缩放 for col, fac inenumerate(factors): B.rescale_col(col, 1/fac)
# 构造多项式列表 H = [] for row in B: poly = sum([c * m for c, m inzip(row, monomials)]) if poly != 0: H.append(poly) I = ideal(H) if I.dimension() == 0: roots = [] for root in I.variety(ring=ZZ): roots.append(tuple(R(root[str(v)]) for v in f.variables())) return roots
return []
n = e = c =
P = Zmod(ZZ(e))["k,s"] k, s = P.gens() f = 1 + k * (n - s) rs = small_roots(f, (2**540, 2**1025), m=4, d=5) print(rs)
k, s = map(int, rs[0]) phi = n - s d = pow(e, -1, phi) m = pow(c, d, n) print(long_to_bytes(int(m)))
from Crypto.Util.number import long_to_bytes import itertools
# --- helper: simulate coefficients_monomials() --- defcoeffs_monos(polys): """ polys: list of polynomials return: matrix of coefficients, list of monomials """ # 收集所有 monomials monos = set() for p in polys: monos |= set(p.monomials()) monos = sorted(monos)
# 构造矩阵 rows = [] for p in polys: c = p.coefficients() m = p.monomials() d = dict(zip(m, c)) row = [d.get(mon, 0) for mon in monos] rows.append(row)
return matrix(ZZ, rows), monos
defsmall_roots(f, bounds, m=1, d=None): ifnot d: d = f.degree()
R = f.base_ring() N = R.cardinality()
lc = f.coefficients().pop(0) f = f / lc f = f.change_ring(ZZ)
G = [] for i inrange(m + 1): base = (N ** (m - i)) * (f ** i) for shifts in itertools.product(range(d), repeat=f.nvariables()): g = base for var, shift inzip(f.variables(), shifts): g *= var ** shift G.append(g)
# scaling factors = [monomial(*bounds) for monomial in monomials] for i, factor inenumerate(factors): B.rescale_col(i, factor)
B = B.LLL() B = matrix(QQ, B)
for i, factor inenumerate(factors): B.rescale_col(i, QQ(1) / factor)
H = [] P_QQ = f.parent().change_ring(QQ)
for row in B.rows(): h = sum([coef * mon for coef, mon inzip(row, monomials)]) h = P_QQ(h) H.append(h)
I = ideal(H) if I.dimension() == -1: H.pop() elif I.dimension() == 0: roots = [] for root in I.variety(ring=ZZ): root = tuple(R(root[var]) for var in f.variables()) roots.append(root) return roots
return []
n = e = c =
P = PolynomialRing(Zmod(e), ["k", "s"]) k, s = P.gens()
f = 1 + k * (n - s)
rs = small_roots(f, (2**490, 2**2050), m=4, d=5) #################### 注意这里要传入k和s的上界,特别要注意三个素数是s的上界 #################### s = n - phi print(rs)
k, s = map(int, rs[0]) phi = n - s
d = pow(e, -1, phi) m = pow(c, d, n)
print(long_to_bytes(int(m)))
1.10common prime RSA
Link:Common Prime RSA 笔记 | 独奏の小屋 g 是一个素数,且p和q也是素数,a和b是随机生成的数 $$ \begin{align} p &= 2ga+1 \ q &= 2gb+1 \ h &= 2gab +a+b \ N &= p * q = 2gh + 1\ N - 1 &= 2gh \end{align} $$ 定义 $\gamma$ 表示共因子g的相对于N的大小,即$g = N^\gamma$ 考虑$g <= N^{1/2}$,故$0 <= \gamma <= 1/2$
import random try: gcd except NameError: from math import gcd
defrho(N): f = lambda x: (pow(x, N-1, N) + 3) % N whileTrue: # 加快入环速度 t = random.randint(2, N) h = f(t) step_times = 0 step_limit = 2 whileTrue: ifnot step_times < step_limit: step_times = 0 step_limit *= 2 t = h h = f(h) p = gcd(abs(int(t) - int(h)), N) if p == N: break elif p > 1: return (p, N // p) else: h = f(h) step_times += 1
print(rho(N))
1.10.2.已知a,b
构造方程 $4abg^2+2(a+b)g-N+1=0$
1 2 3 4 5 6 7 8 9 10 11 12 13
a = 1185431345934512 b = 1989628969125971 N = 54692260436051338814890781701826055707958209029414126894070449935683071253184867947357262267840171428710181955973010913204514025135188192484651672240708141692701667242130748316666406528479191422804307020656050201187035928715833163999813216597718706449260040885862566373392398826670863398295350419792842640631 P.<g> = ZZ[] f = 4 * a * b * g ^ 2 + 2 * (a + b) * g - N + 1 g = f.roots() if g: g = g[0][0] p = 2 * g * a + 1 q = 2 * g * b + 1 assert p * q == N print('p =',p) print('q =',q)
g = 2056971706333850947354991471886113601423457483931388832864204860321308350537317091564919029078296379733989138742162694786565228112885684303 N = 67324909911911622626246005558967775211455024820506932698435813321567574468019013664789401988015894964099052816176029553245881317276340043887466584645914352982274378611180595397686920214079479901514703963131435008906250160656759300390805929849374653321934393399433471228218819498373221757779799476717494079667 M = (N - 1) // (2 * g) c = M % g P.<a> = ZZ[] f = 2 * g * a ^ 2 - 2 * g * c * a + M - c a = f.roots() if a: a, b = a[0][0], a[1][0] p = 2 * g * a + 1 q = 2 * g * b + 1 assert p * q == N print('p =',p) print('q =',q)
g=a+b
$N=2g(2gab+a+b)+1$ $(N-1)/ (4g^2) = ab + 1/2$ 带入$b=g-a$得: $2a^2-2ga+(N-1)/2g^2-1=0$
1 2 3 4 5 6 7 8 9 10 11 12 13
g = 2855372645569408464444580237486670388029956719716115953907612135874419892154982850222965560661211729647325085879529571229774148545656169021 N = 159549169988238873893531105042878385551537587717347282632324748268846735710748763722602882823022008548774298858161130258369850715542192739582830583643642436399008902770027668038725347353393047833875066622910131525247842517372845617227325882916166114361718015983671803859502931814932543107911548450229250776542101141849788751722460468073974316977656001286989710480324512919121409123619799426221232443036698458643438020098037548757403 M = (N - 1) // (2 * g) P.<a> = ZZ[] f = 2 * a ^ 2 - 2 * g * a + (N - 1) // (2 * g ^ 2) - 1 a = f.roots() if a: a, b = a[0][0], a[1][0] p = 2 * g * a + 1 q = 2 * g * b + 1 assert p * q == N print('p =',p) print('q =',q)
M = (N - 1) // (2 * g) u = M // (2 * g) v = M - 2 * g * u GF = Zmod(N) x = GF.random_element() y = x ** (2 * g) # c的范围大概与N^(0.5-2*gamma)很接近 lower = ZZ(2**(cbits-1)) upper = ZZ(2**(cbits+1)) c = bsgs(y, y ** u, (lower, upper)) ab = u - c apb = v + 2 * g * c P = PolynomialRing(ZZ, 'x') x = P.gen() f = x ** 2 - apb * x + ab a = f.roots() if a: a, b = a[0][0], a[1][0] p = 2 * g * a + 1 q = 2 * g * b + 1 assert p * q == N
from Crypto.Util.number import * withopen("/home/ctf/flag", "r") as f: flag = f.read().strip().encode()
defcreate(): pl = [] for i inrange(3): pl.append(getPrime(1024)) returnsorted(pl)
defencrypt(pl, flag): m = flag p, q, r = pl[0], pl[1], pl[2] n = p * q * r phi = (p-1) * (q-1) * (r-1) e = 65537 phi_2 = (p-1) * (q-1) n2 = p * q c = pow(bytes_to_long(m), e, n2)
n = 125474178 phi = 12547417 c = 3284379 import gmpy2 from Crypto.Util.number import long_to_bytes import random import sys
e = 65537
defdecompose_power_of_two(x): """返回 (t, s) 使得 x = 2^t * s, s odd""" t = 0 s = x while s % 2 == 0: t += 1 s //= 2 return t, s
deffind_nontrivial_k(phi_val, n_val, trials=256): """随机尝试基 y,计算 y^(phi/2^i) 并返回第一个满足非平凡条件的 k""" t, _ = decompose_power_of_two(phi_val) for attempt inrange(trials): # 随机基(避免过大基) y = random.randrange(2, min(1 << 30, n_val - 2)) # if gmpy2.gcd(y, n_val) != 1: # continue for i in range(1, t + 1): exp = phi_val // (2 ** i) k = int(pow(y, exp, n_val)) if k != 1and k != (n_val - 1): return k returnNone
deftry_extract_factor_from_k(n_val, k): """对 k 同时尝试 gcd(n, k-1) 与 gcd(n, k+1),返回所有在 (1,n) 之间的非平凡因子列表""" res = [] for delta in (-1, 1): g = int(gmpy2.gcd(n_val, k + delta)) if1 < g < n_val: res.append(g) returnlist(set(res)) # 去重
defrecover_factors_and_decrypt(): print("[*] 开始在 n 上寻找非平凡平方根 k ...") # 在 n 上找 k k = find_nontrivial_k(phi, n, trials=1024) if k isNone: print("[-] 未能在 n 上找到非平凡 k(尝试增大 trials)。") returnFalse print("[+] 找到 k:", k)
if __name__ == "__main__": ok = recover_factors_and_decrypt() ifnot ok: sys.exit(1)
**解法二:
1 2 3 4 5 6 7 8 9 10 11
# sage n = phi = c = e = 65537 d = inverse(e,phi) s = pow(c,d,n) R.<x> = PolynomialRing(Zmod(n)) f = x-s res = f.small_roots(x=2^2048,beta = 0.5, epsilon = 0.05) print(long_to_bytes(int(res[0])).decode())