v 的长度是可以构造的,而上界是固定的,越接近上界,值越精确,故可以通过系数调整 |v| 的值(配平)从而使得和上界更接近;但是存在问题,如果系数过大,使得长度超过了上界,则无法求解。
构造示例
bm−kn−c=−r⟹[m−1−k][10b02400c00n]=[m−2400−r]
使目标向量中的值数量级相近会更利于规约,对格进行配平(乘大系数T)后规约,有机会得到目标向量。
LLL加速:flatter
1 2 3 4 5 6 7 8 9 10
from subprocess import check_output from re import findall
defflatter(M): # compile https://github.com/keeganryan/flatter and put it in $PATH z = "[[" + "]\n[".join(" ".join(map(str, row)) for row in M) + "]]" ret = check_output(["flatter"], input=z.encode()) return matrix(M.nrows(), M.ncols(), map(int, findall(b"-?\\d+", ret)))
#Sage #多项式1 N = p = q = Q.<x> = Zmod(q)[] P.<y> = Zmod(p)[]
ex = hx =
print('-------decrypt------') qq = x^N-1 pp = y^N-1 hn = [int(x) for x in hx.coefficients()] n = len(hn) A1 = matrix.identity(n) A0 = matrix.zero(n) Aq = matrix.identity(n) * q Ah = matrix(ZZ, [hn[-i:] + hn[:-i] for i inrange(n)]) M = block_matrix([A1,Ah,A0,Aq],nrows=2) L = M.LLL() v = L[0] f = list(v)[:n] g = list(v)[n:] fx = Q(f) fy = P(f) gx = Q(g) Fqx = fx.inverse_mod(qq) Fpy = fy.inverse_mod(pp)
#hxx = (Fqx*gx).mod(x^N-1) #print(hxx==hx)
ax = (fx*ex).mod(qq) an = [int(x) for x in ax.coefficients()] #中心提升(centerlift),使域范围从[0,q)变换到(-q/2,q/2) for i inrange(len(an)): if an[i] > q//2: an[i] -= q ax = P(an) print(ax) out = (Fpy * ax).mod(pp) print(out)
definv_mod_prime(f,p): T = Zx.change_ring(Integers(p)).quotient(x^n-1) return Zx(lift(1 / T(f)))
defmul(f,g): return (f * g) % (x^n-1)
defbal_mod(f,q): g = list(((f[i] + q//2) % q) - q//2for i inrange(n)) return Zx(g)
defdecrypt(e,pri_key): f,fp = pri_key a = bal_mod(mul(e,f),q) d = bal_mod(mul(a,fp),p) return d
defget_key(): for j inrange(2 * n): try: f = Zx(list(M[j][:n])) fp = inv_mod_prime(f,p) return (f,fp) except: pass return (f,f)
M = matrix(ZZ, 2*n, 2*n) hh = h.list() for i inrange(n): M[i,i] = 1 for i inrange(n,2*n): M[i,i] = q for i inrange(n): for j inrange(n): M[i,j+n] = hh[(n-i+j) % n] M = M.LLL() key = get_key()
l = decrypt(e, key).list() flag = bytes(l) print(flag)
#脚本1-小规模 #Sage from sage.modules.free_module_integer import IntegerLattice
row = column = prime =
ma = res =
W = matrix(ZZ, ma) cc = vector(ZZ, res)
# Babai's Nearest Plane algorithm defBabai_closest_vector(M, G, target): small = target for _ inrange(5): for i inreversed(range(M.nrows())): c = ((small * G[i]) / (G[i] * G[i])).round() small -= M[i] * c return target - small
A1 = matrix.identity(column) Ap = matrix.identity(row) * prime B = block_matrix([[Ap], [W]]) lattice = IntegerLattice(B, lll_reduce=True) print("LLL done") gram = lattice.reduced_basis.gram_schmidt()[0] target = vector(ZZ, res) re = Babai_closest_vector(lattice.reduced_basis, gram, target) print("Closest Vector: {}".format(re))
R = IntegerModRing(prime) M = Matrix(R, ma) M = M.transpose()
#脚本2-大规模 #Sage from sage.modules.free_module_integer import IntegerLattice from random import randint import sys from itertools import starmap from operator import mul
# Babai's Nearest Plane algorithm # from: http://mslc.ctf.su/wp/plaidctf-2016-sexec-crypto-300/ defBabai_closest_vector(M, G, target): small = target for _ inrange(1): for i inreversed(range(M.nrows())): c = ((small * G[i]) / (G[i] * G[i])).round() small -= M[i] * c return target - small
m = n = q =
A_values = b_values =
A = matrix(ZZ, m + n, m) for i inrange(m): A[i, i] = q for x inrange(m): for y inrange(n): A[m + y, x] = A_values[x][y] lattice = IntegerLattice(A, lll_reduce=True) print("LLL done") gram = lattice.reduced_basis.gram_schmidt()[0] target = vector(ZZ, b_values) res = Babai_closest_vector(lattice.reduced_basis, gram, target) print("Closest Vector: {}".format(res))
R = IntegerModRing(q) M = Matrix(R, A_values) ingredients = M.solve_right(res)
print("Ingredients: {}".format(ingredients))
for row, b inzip(A_values, b_values): effect = sum(starmap(mul, zip(map(int, ingredients), row))) % q assert(abs(b - effect) < 2 ** 37)
# Sage DEBUG = False m = 44 n = 55 p = 2^5 q = 2^10
deferrorV(): return vector(ZZ, [1 - randrange(3) for _ inrange(n)])
defvecM(): return vector(ZZ, [p//2 - randrange(p) for _ inrange(m)])
defvecN(): return vector(ZZ, [p//2 - randrange(p) for _ inrange(n)])
defmatrixMn(): mt = matrix(ZZ, [[q//2 - randrange(q) for _ inrange(n)] for _ inrange(m)]) return mt
A = matrixMn() e = errorV() x = vecM() b = x*A+e
if DEBUG: print('A = \n%s' % A) print('x = %s' % x) print('b = %s' % b) print('e = %s' % e)
z = matrix(ZZ, [0for _ inrange(m)]).transpose() beta = matrix(ZZ, [1]) T = block_matrix([[A, z], [matrix(b), beta]]) if DEBUG: print('T = \n%s' % T)
print('-----') L = T.LLL() print(L[0]) print(L[0][:n] == e)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
# Sage import re s2n=lambda x: [int(x) for x in re.findall(r"\-?\d+\.?\d*",x)] f=open("./enc.out","r").readlines() m = 66 n = 200 p = 5 q = 2^20 B = [s2n(f[i]) for i inrange(m)] A = [s2n(f[i+66]) for i inrange(m)] C = [s2n(f[i+132]) for i inrange(m)] b= list(matrix(ZZ,s2n(f[-1]))) m=A+B+C+b M = matrix(ZZ,m) L = M.LLL() print(L[0]) res=M.solve_left(L[0]) for i in res[:-1]: print(chr(abs(i)),end="")
defallpmones(v): returnlen([vj for vj in v if vj in [-1, 0, 1]]) == len(v)
# We generate the lattice of vectors orthogonal to b modulo x0 deforthoLattice(b, x0): m = b.length() M = Matrix(ZZ, m, m)
for i inrange(1, m): M[i, i] = 1 M[1:m, 0] = -b[1:m] * inverse_mod(b[0], x0) M[0, 0] = x0
for i inrange(1, m): M[i, 0] = mod(M[i, 0], x0)
return M
defallones(v): iflen([vj for vj in v if vj in [0, 1]]) == len(v): return v iflen([vj for vj in v if vj in [0, -1]]) == len(v): return -v returnNone
defrecoverBinary(M5): lv = [allones(vi) for vi in M5 if allones(vi)] n = M5.nrows() for v in lv: for i inrange(n): nv = allones(M5[i] - v) if nv and nv notin lv: lv.append(nv) nv = allones(M5[i] + v) if nv and nv notin lv: lv.append(nv) return Matrix(lv)
defkernelLLL(M): n = M.nrows() m = M.ncols() if m < 2 * n: return M.right_kernel().matrix() K = 2 ^ (m // 2) * M.height()
MB = Matrix(ZZ, m + n, m) MB[:n] = K * M MB[n:] = identity_matrix(m)
MB2 = MB.T.LLL().T
assert MB2[:n, : m - n] == 0 Ke = MB2[n:, : m - n].T
return Ke
defattack(m, n, p, h): # This is the Nguyen-Stern attack, based on BKZ in the second step print("n =", n, "m =", m)
iota = 0.035 nx0 = int(2 * iota * n ^ 2 + n * log(n, 2)) print("nx0 =", nx0)
v1.5.2