defgenHssp(m, n, M): R.<z> = PolynomialRing(Zmod(M)) x = [R([randint(0, 1) for mi inrange(m)]) for ni inrange(n)] a = [randint(0, M-1) for ni inrange(n)] h = sum([a[i] * x[i] for i inrange(n)]) return (a, [xi.list() for xi in x]), (M, h.list())
m = 200 n = 100 M = 199999999999997 (a, X), (M, h) = genHssp(m, n, M)
# https://github.com/Neobeo/HackTM2023/blob/main/solve420.sage # faster LLL reduction to replace `M.LLL()` wiith `flatter(M)` defflatter(M): from subprocess import check_output from re import findall M = matrix(ZZ,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)))
Coron, Jean-Sébastien, and Agnese Gini. “A polynomial-time algorithm for solving the hidden subset sum problem.” Annual International Cryptology Conference. Cham: Springer International Publishing, 2020.
Lx = Lx[1:] E = matrix(ZZ, [[1for c inrange(Lxc.ncols())] for r inrange(Lxc.nrows())]) Lx = (Lx + E) / 2
Lx2 = [] e = vector(ZZ, [1] * m) rsc = Lxc.row_space() for lx in Lx: if lx in rsc: Lx2 += [lx] continue lx = e - lx if lx in rsc: Lx2 += [lx] continue print('Something wrong?') Lx = matrix(Zmod(M), Lx2) vh = vector(Zmod(M), h) va = Lx.solve_left(vh)
# https://github.com/Neobeo/HackTM2023/blob/main/solve420.sage # faster LLL reduction to replace `M.LLL()` wiith `flatter(M)` defflatter(M, **kwds): from subprocess import check_output from re import findall M = matrix(ZZ,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)))
defgenHssp(m, n, M): R.<z> = PolynomialRing(Zmod(M)) x = [R([randint(0, 1) for mi inrange(m)]) for ni inrange(n)] a = [randint(0, M-1) for ni inrange(n)] h = sum([a[i] * x[i] for i inrange(n)]) return (a, [xi.list() for xi in x]), (M, h.list())
defcheckMatrix(M, wl=[-1, 1]): M = [list(_) for _ inlist(M)] ml = list(set(flatten(M))) logging.debug(ml) returnsorted(ml) == sorted(wl)
defNguyen_Stern(h, m, n, M): B = matrix(ZZ, m) B[0, 0] = M h0i = Integer(h[0]).inverse_mod(M) for i inrange(1, m): B[i, 0] = - h[i] * h0i B[i, i] = 1 #L = B.BKZ() # slooooooow L = flatter(B) logging.info('flatter done.')
''' vh = vector(Zmod(M), h) logging.debug([vector(Zmod(M), list(l)) * vh for l in L]) '''
''' try: Lx_real = matrix(ZZ, [xi + [0] * (m - len(xi)) for xi in X]) rsc = Lxc.row_space() logging.debug([xi in rsc for xi in Lx_real]) except: pass '''
e = matrix(ZZ, [1] * m) B = block_matrix([[-e], [2*Lxc]]) Lx = B.BKZ() logging.info('BKZ done.') assert checkMatrix(Lx) assertlen(set(Lx[0])) == 1
Lx = Lx[1:] E = matrix(ZZ, [[1for c inrange(Lxc.ncols())] for r inrange(Lxc.nrows())]) Lx = (Lx + E) / 2
Lx2 = [] e = vector(ZZ, [1] * m) rsc = Lxc.row_space() for lx in Lx: if lx in rsc: Lx2 += [lx] continue lx = e - lx if lx in rsc: Lx2 += [lx] continue logging.warning('Something wrong?') Lx = matrix(Zmod(M), Lx2)
vh = vector(Zmod(M), h) va = Lx.solve_left(vh) return Lx, va
# stolen from https://github.com/tl2cents/Implementation-of-Cryptographic-Attacks/blob/main/MultivariateHSSP/A%20Polynomial-Time%20Algorithm%20for%20Solving%20the%20Hidden%20Subset%20Sum%20Problem.ipynb defderive_M(n): iota=0.035 Mbits=int(2 * iota * n^2 + n * log(n,2)) M = random_prime(2^Mbits, proof = False, lbound = 2^(Mbits - 1)) return Integer(M)
m = 200 n = 100 M = derive_M(n) (a, X), (M, h) = genHssp(m, n, M) logging.debug('m: %d | n: %d' % (m, n)) logging.debug('%s, %s' % (M, M.nbits()))
Lx, va = Nguyen_Stern(h, m, n, M) print(sorted(va) == sorted(a))