def id_dirichlet(fun, N, n): N = Integer(N) if N == 1: return (1, 1) p2 = valuation(N, 2) N2 = 2**p2 Nodd = N // N2 Nfact = list(factor(Nodd)) #print "n = "+str(n) #for j in range(20): # print "chi(%d) = e(%d/%d)"%(j+2, fun(j+2,n), n) plist = [z[0] for z in Nfact] ppows = [z[0]**z[1] for z in Nfact] ppows2 = list(ppows) idems = [1 for z in Nfact] proots = [primitive_root(z) for z in ppows] # Get CRT idempotents if p2 > 0: ppows2.append(N2) for j in range(len(plist)): exps = [1 for z in idems] if p2 > 0: exps.append(1) exps[j] = proots[j] idems[j] = crt(exps, ppows2) idemvals = [fun(z, n) for z in idems] # now normalize to right root of unity base idemvals = [ idemvals[j] * euler_phi(ppows[j]) / n for j in range(len(idemvals)) ] ans = [ Integer(mod(proots[j], ppows[j])**idemvals[j]) for j in range(len(proots)) ] ans = crt(ans, ppows) # There are cases depending on 2-part of N if p2 == 0: return (N, ans) if p2 == 1: return (N, crt([1, ans], [2, Nodd])) if p2 == 2: my3 = crt([3, 1], [N2, Nodd]) if fun(my3, n) == 0: return (N, crt([1, ans], [4, Nodd])) else: return (N, crt([3, ans], [4, Nodd])) # Final case 2^3 | N my5 = crt([5, 1], [N2, Nodd]) test1 = fun(my5, n) * N2 / 4 / n test1 = Integer(mod(5, N2)**test1) minusone = crt([-1, 1], [N2, Nodd]) test2 = (fun(minusone, n) * N2 / 4 / n) % (N2 / 4) if test2 > 0: test1 = Integer(mod(-test1, N2)) return (N, crt([test1, ans], [N2, Nodd]))
def id_dirichlet(fun, N, n): N = Integer(N) if N == 1: return (1, 1) p2 = valuation(N, 2) N2 = 2 ** p2 Nodd = N / N2 Nfact = list(factor(Nodd)) # print "n = "+str(n) # for j in range(20): # print "chi(%d) = e(%d/%d)"%(j+2, fun(j+2,n), n) plist = [z[0] for z in Nfact] ppows = [z[0] ** z[1] for z in Nfact] ppows2 = list(ppows) idems = [1 for z in Nfact] proots = [primitive_root(z) for z in ppows] # Get CRT idempotents if p2 > 0: ppows2.append(N2) for j in range(len(plist)): exps = [1 for z in idems] if p2 > 0: exps.append(1) exps[j] = proots[j] idems[j] = crt(exps, ppows2) idemvals = [fun(z, n) for z in idems] # now normalize to right root of unity base idemvals = [idemvals[j] * euler_phi(ppows[j]) / n for j in range(len(idemvals))] ans = [Integer(mod(proots[j], ppows[j]) ** idemvals[j]) for j in range(len(proots))] ans = crt(ans, ppows) # There are cases depending on 2-part of N if p2 == 0: return (N, ans) if p2 == 1: return (N, crt([1, ans], [2, Nodd])) if p2 == 2: my3 = crt([3, 1], [N2, Nodd]) if fun(my3, n) == 0: return (N, crt([1, ans], [4, Nodd])) else: return (N, crt([3, ans], [4, Nodd])) # Final case 2^3 | N my5 = crt([5, 1], [N2, Nodd]) test1 = fun(my5, n) * N2 / 4 / n test1 = Integer(mod(5, N2) ** test1) minusone = crt([-1, 1], [N2, Nodd]) test2 = (fun(minusone, n) * N2 / 4 / n) % (N2 / 4) if test2 > 0: test1 = Integer(mod(-test1, N2)) return (N, crt([test1, ans], [N2, Nodd]))
def _polynomial_gcd_crt(a, b, modulus): gs = [] ps = [] for p, _ in factor(modulus): zmodp = Zmod(p) gs.append(_polynomial_gcd(a.change_ring(zmodp), b.change_ring(zmodp)).change_ring(ZZ)) ps.append(p) return gs[0] if len(gs) == 1 else crt(gs, ps)
def _backtrack(s, bases, residues, moduli, i): if i == len(s): combined_modulus = 1 for modulus in moduli: combined_modulus *= modulus return crt(residues, moduli), combined_modulus moduli.append(4 * bases[i]) for residue in s[i]: residues.append(residue) try: crt(residues, moduli) ans = _backtrack(s, bases, residues, moduli, i + 1) if ans: return ans except ValueError: pass residues.pop() moduli.pop() return None, None
def pohlighellman(g, h): phi = g.multiplicative_order() factors = factor(phi) chinese_pairs = [] for pi, ei in factors: n = phi / (pi**ei) print("testing n = %s" % n) hn = h**n print(("Searching h^%d in subgroup " "g^%d using Baby-step giant-step") % (n, n)) a = babystepgiantstep(g**n, hn) print("Found g^(%s * %s) == %s" % (n, a, hn)) chinese_pairs.append([a, pi**ei]) return crt(*map(list, zip(*chinese_pairs)))
def attack(base, multiplication_result): """ Solves the discrete logarithm problem using the MOV attack. :param base: the base point :param multiplication_result: the point multiplication result :return: l such that l * base == multiplication_result """ curve = base.curve() p = curve.base_ring().order() n = base.order() assert gcd( n, p ) == 1, "GCD of curve base ring order and generator order should be 1." logging.debug("Calculating embedding degree...") # Embedding degree k. k = 1 while (p**k - 1) % n != 0: k += 1 logging.debug( f"Found embedding degree {k}, computing discrete logarithm...") pairing_curve = curve.base_extend(GF(p**k)) pairing_base = pairing_curve(base) pairing_multiplication_result = pairing_curve(multiplication_result) ls = [] ds = [] while lcm(*ds) != n: rand = pairing_curve.random_point() o = rand.order() d = gcd(o, n) rand = (o // d) * rand assert rand.order() == d u = pairing_base.weil_pairing(rand, n) v = pairing_multiplication_result.weil_pairing(rand, n) logging.debug(f"Calculating ({v}).log({u}) modulo {d}") l = v.log(u) logging.debug(f"Found discrete log {l} modulo {d}") ls.append(int(l)) ds.append(int(d)) return ls[0] if len(ls) == 1 else int(crt(ls, ds))
def pohlighellman(g, h): phi = g.multiplicative_order() factors = factor(phi) chinese_pairs = [] for pi, ei in factors: n = phi / (pi ** ei) print("testing n = %s" % n) hn = h ** n print("h^%s = %s" % (n, hn)) for i in range(pi ** ei): print("Testing g^(%s * %s) == %s" % (i, n, hn)) if g ** (n * i) == hn: print("Found x mod %s = %s" % (pi ** ei, i)) chinese_pairs.append([i, pi ** ei]) break return crt(*map(list, zip(*chinese_pairs)))
x = X[i] b = B[i] z = log(Integer(x), b) Z[i] = int(z) d = {"X": X, "B": B, "Z": Z} save_pickle(d, FOLDER, "ilog.pkl") set_seed(SEED + 108) N = [random.randint(2, 6) for _ in range(40)] X = [[random.randint(0, 1000) for _ in range(N[i])] for i in range(20)] + [[random.randint(0, 1_000_000) for _ in range(N[20 + i])] for i in range(20)] # Remainder Y = [[random.randint(10, 1000) for _ in range(N[i])] for i in range(20)] + [[random.randint(1000, 1_000_000) for _ in range(N[20 + i])] for i in range(20)] # Modulus Z = [0,]*len(X) # The solution for i in range(len(X)): X[i] = [X[i][j] % Y[i][j] for j in range(len(X[i]))] # Ensure a is within [0, m) try: z = crt(X[i], Y[i]) Z[i] = int(z) except: Z[i] = None d = {"X": X, "Y": Y, "Z": Z} save_pickle(d, FOLDER, "crt.pkl") ############################################################################### # Number theory functions ############################################################################### set_seed(SEED + 201) X = [random.randint(1, 1_000_000_000) for _ in range(20)] Z = [0,]*len(X) for i in range(len(X)):
def make_luts(field, sub_folder, seed, sparse=False): global FIELD, RING print(f"Making LUTs for {field}") ############################################################################### # Finite field arithmetic ############################################################################### folder = os.path.join(PATH, "fields", "data", sub_folder) if os.path.exists(folder): shutil.rmtree(folder) os.mkdir(folder) FIELD = field RING = PolynomialRing(field, names="x") characteristic = int(field.characteristic()) order = int(field.order()) dtype = np.int64 if order <= np.iinfo(np.int64).max else object alpha = field.primitive_element() # assert field.gen() == field.multiplicative_generator() d = { "characteristic": int(field.characteristic()), "degree": int(field.degree()), "order": int(field.order()), "primitive_element": I(field.primitive_element()), "irreducible_poly": [int(c) for c in field.modulus().list()[::-1]] } save_json(d, folder, "properties.json", indent=True) set_seed(seed + 1) X, Y, XX, YY, ZZ = io_2d(0, order, 0, order, sparse=sparse) for i in range(ZZ.shape[0]): for j in range(ZZ.shape[1]): ZZ[i, j] = I(F(XX[i, j]) + F(YY[i, j])) d = {"X": X, "Y": Y, "Z": ZZ} save_pickle(d, folder, "add.pkl") set_seed(seed + 2) X, Y, XX, YY, ZZ = io_2d(0, order, 0, order, sparse=sparse) for i in range(ZZ.shape[0]): for j in range(ZZ.shape[1]): ZZ[i, j] = I(F(XX[i, j]) - F(YY[i, j])) d = {"X": X, "Y": Y, "Z": ZZ} save_pickle(d, folder, "subtract.pkl") set_seed(seed + 3) X, Y, XX, YY, ZZ = io_2d(0, order, 0, order, sparse=sparse) for i in range(ZZ.shape[0]): for j in range(ZZ.shape[1]): ZZ[i, j] = I(F(XX[i, j]) * F(YY[i, j])) d = {"X": X, "Y": Y, "Z": ZZ} save_pickle(d, folder, "multiply.pkl") set_seed(seed + 4) X, Y, XX, YY, ZZ = io_2d(0, order, -order - 2, order + 3, sparse=sparse) for i in range(ZZ.shape[0]): for j in range(ZZ.shape[1]): ZZ[i, j] = I(F(XX[i, j]) * YY[i, j]) d = {"X": X, "Y": Y, "Z": ZZ} save_pickle(d, folder, "scalar_multiply.pkl") set_seed(seed + 5) X, Y, XX, YY, ZZ = io_2d(0, order, 1, order, sparse=sparse) for i in range(ZZ.shape[0]): for j in range(ZZ.shape[1]): ZZ[i, j] = I(F(XX[i, j]) / F(YY[i, j])) d = {"X": X, "Y": Y, "Z": ZZ} save_pickle(d, folder, "divide.pkl") set_seed(seed + 6) X, Z = io_1d(0, order, sparse=sparse) for i in range(X.shape[0]): Z[i] = I(-F(X[i])) d = {"X": X, "Z": Z} save_pickle(d, folder, "additive_inverse.pkl") set_seed(seed + 7) X, Z = io_1d(1, order, sparse=sparse) for i in range(X.shape[0]): Z[i] = I(1 / F(X[i])) d = {"X": X, "Z": Z} save_pickle(d, folder, "multiplicative_inverse.pkl") set_seed(seed + 8) X, Y, XX, YY, ZZ = io_2d(1, order, -order - 2, order + 3, sparse=sparse) for i in range(ZZ.shape[0]): for j in range(ZZ.shape[1]): ZZ[i, j] = I(F(XX[i, j])**YY[i, j]) d = {"X": X, "Y": Y, "Z": ZZ} save_pickle(d, folder, "power.pkl") set_seed(seed + 9) X, Z = io_1d(1, order, sparse=sparse) for i in range(Z.shape[0]): try: Z[i] = I(field.fetch_int(X[i]).log(alpha)) except: Z[i] = I(log(F(X[i]), alpha)) d = {"X": X, "Z": Z} save_pickle(d, folder, "log.pkl") set_seed(seed + 10) X, Z = io_1d(0, order, sparse=sparse) for i in range(X.shape[0]): Z[i] = int(F(X[i]).additive_order()) d = {"X": X, "Z": Z} save_pickle(d, folder, "additive_order.pkl") set_seed(seed + 11) X, Z = io_1d(1, order, sparse=sparse) for i in range(X.shape[0]): Z[i] = int(F(X[i]).multiplicative_order()) d = {"X": X, "Z": Z} save_pickle(d, folder, "multiplicative_order.pkl") set_seed(seed + 12) X, _ = io_1d(0, order, sparse=sparse) Z = [] for i in range(len(X)): x = F(X[i]) p = x.charpoly() z = poly_to_list(p) Z.append(z) d = {"X": X, "Z": Z} save_pickle(d, folder, "characteristic_poly_element.pkl") set_seed(seed + 13) shapes = [(2, 2), (3, 3), (4, 4), (5, 5), (6, 6)] X = [] Z = [] for i in range(len(shapes)): x = randint_matrix(0, order, shapes[i]) X.append(x) x = matrix(FIELD, [[F(e) for e in row] for row in x]) p = x.charpoly() z = poly_to_list(p) Z.append(z) d = {"X": X, "Z": Z} save_pickle(d, folder, "characteristic_poly_matrix.pkl") set_seed(seed + 14) X, _ = io_1d(0, order, sparse=sparse) Z = [] for i in range(len(X)): x = F(X[i]) p = x.minpoly() z = poly_to_list(p) Z.append(z) d = {"X": X, "Z": Z} save_pickle(d, folder, "minimal_poly_element.pkl") # set_seed(seed + 15) # shapes = [(2,2), (3,3), (4,4), (5,5), (6,6)] # X = [] # Z = [] # for i in range(len(shapes)): # x = randint_matrix(0, order, shapes[i]) # X.append(x) # x = matrix(FIELD, [[F(e) for e in row] for row in x]) # p = x.minpoly() # z = np.array([I(e) for e in p.list()[::-1]], dtype=dtype).tolist() # z = z if z != [] else [0] # Z.append(z) # d = {"X": X, "Z": Z} # save_pickle(d, folder, "minimal_poly_matrix.pkl") set_seed(seed + 16) X, Z = io_1d(0, order, sparse=sparse) for i in range(X.shape[0]): z = F(X[i]).trace() Z[i] = int(z) d = {"X": X, "Z": Z} save_pickle(d, folder, "field_trace.pkl") set_seed(seed + 17) X, Z = io_1d(0, order, sparse=sparse) for i in range(X.shape[0]): z = F(X[i]).norm() Z[i] = int(z) d = {"X": X, "Z": Z} save_pickle(d, folder, "field_norm.pkl") ############################################################################### # Linear algebra ############################################################################### set_seed(seed + 201) X_shapes = [(2, 2), (2, 3), (3, 2), (3, 3), (3, 4)] Y_shapes = [(2, 2), (3, 3), (2, 4), (3, 3), (4, 5)] X = [] Y = [] Z = [] for i in range(len(shapes)): x = randint_matrix(0, order, X_shapes[i]) y = randint_matrix(0, order, Y_shapes[i]) X.append(x) Y.append(y) x = matrix(FIELD, [[F(e) for e in row] for row in x]) y = matrix(FIELD, [[F(e) for e in row] for row in y]) z = x * y z = np.array([[I(e) for e in row] for row in z], dtype) Z.append(z) d = {"X": X, "Y": Y, "Z": Z} save_pickle(d, folder, "matrix_multiply.pkl") set_seed(seed + 202) shapes = [(2, 2), (2, 3), (3, 2), (3, 3), (3, 4)] X = [] Z = [] for i in range(len(shapes)): x = randint_matrix(0, order, shapes[i]) X.append(x) x = matrix(FIELD, [[F(e) for e in row] for row in x]) z = x.rref() z = np.array([[I(e) for e in row] for row in z], dtype) Z.append(z) d = {"X": X, "Z": Z} save_pickle(d, folder, "row_reduce.pkl") set_seed(seed + 203) shapes = [(2, 2), (2, 3), (3, 2), (3, 3), (3, 4), (4, 3), (4, 4), (4, 5), (5, 4), (5, 5), (5, 6), (6, 5), (6, 6)] X = [] L = [] U = [] for i in range(len(shapes)): while True: # Ensure X has a PLU decomposition with P = I, which means it has an LU decomposition x = randint_matrix(0, order, shapes[i]) x_orig = x.copy() x = matrix(FIELD, [[F(e) for e in row] for row in x]) p, l, u = x.LU() if p == matrix.identity(FIELD, shapes[i][0]): break X.append(x_orig) l = np.array([[I(e) for e in row] for row in l], dtype) u = np.array([[I(e) for e in row] for row in u], dtype) L.append(l) U.append(u) d = {"X": X, "L": L, "U": U} save_pickle(d, folder, "lu_decompose.pkl") set_seed(seed + 204) shapes = [(2, 2), (2, 3), (3, 2), (3, 3), (3, 4), (4, 3), (4, 4), (4, 5), (5, 4), (5, 5), (5, 6), (6, 5), (6, 6)] X = [] L = [] U = [] P = [] for i in range(len(shapes)): x = randint_matrix(0, order, shapes[i]) X.append(x) x = matrix(FIELD, [[F(e) for e in row] for row in x]) p, l, u = x.LU() p = np.array([[I(e) for e in row] for row in p], dtype) l = np.array([[I(e) for e in row] for row in l], dtype) u = np.array([[I(e) for e in row] for row in u], dtype) P.append(p) L.append(l) U.append(u) d = {"X": X, "P": P, "L": L, "U": U} save_pickle(d, folder, "plu_decompose.pkl") set_seed(seed + 205) shapes = [(2, 2), (3, 3), (4, 4), (5, 5), (6, 6)] X = [] Z = [] for i in range(len(shapes)): while True: x = randint_matrix(0, order, shapes[i]) x_orig = x.copy() x = matrix(FIELD, [[F(e) for e in row] for row in x]) if x.rank() == shapes[i][0]: break X.append(x_orig) z = x.inverse() z = np.array([[I(e) for e in row] for row in z], dtype) Z.append(z) d = {"X": X, "Z": Z} save_pickle(d, folder, "matrix_inverse.pkl") set_seed(seed + 206) shapes = [(2, 2), (2, 2), (2, 2), (3, 3), (3, 3), (3, 3), (4, 4), (4, 4), (4, 4), (5, 5), (5, 5), (5, 5), (6, 6), (6, 6), (6, 6)] X = [] Z = [] for i in range(len(shapes)): x = randint_matrix(0, order, shapes[i]) X.append(x) x = matrix(FIELD, [[F(e) for e in row] for row in x]) z = I(x.determinant()) Z.append(z) d = {"X": X, "Z": Z} save_pickle(d, folder, "matrix_determinant.pkl") set_seed(seed + 207) shapes = [(2, 2), (2, 2), (2, 2), (3, 3), (3, 3), (3, 3), (4, 4), (4, 4), (4, 4), (5, 5), (5, 5), (5, 5), (6, 6), (6, 6), (6, 6)] X = [] Y = [] Z = [] for i in range(len(shapes)): while True: x = randint_matrix(0, order, shapes[i]) x_orig = x.copy() x = matrix(FIELD, [[F(e) for e in row] for row in x]) if x.rank() == shapes[i][0]: break X.append(x_orig) y = randint_matrix(0, order, shapes[i][1]) # 1-D vector Y.append(y) y = vector(FIELD, [F(e) for e in y]) z = x.solve_right(y) z = np.array([I(e) for e in z], dtype) Z.append(z) d = {"X": X, "Y": Y, "Z": Z} save_pickle(d, folder, "matrix_solve.pkl") set_seed(seed + 208) shapes = [(2, 2), (2, 3), (2, 4), (3, 2), (4, 2), (3, 3)] X = [] Z = [] for i in range(len(shapes)): deg = shapes[i][1] # The degree of the vector space # Random matrix x = randint_matrix(0, order, shapes[i]) X.append(x) x = matrix(FIELD, [[F(e) for e in row] for row in x]) z = x.row_space() if z.dimension() == 0: z = randint_matrix(0, 1, (0, deg)) else: z = z.basis_matrix() z = np.array([[I(e) for e in row] for row in z], dtype) Z.append(z) # Reduce the row space by 1 by copying the 0th row to the jth row for j in range(1, shapes[i][0]): x = copy(x) x[j, :] = F(random.randint(0, order - 1)) * x[0, :] z = x.row_space() if z.dimension() == 0: z = randint_matrix(0, 1, (0, deg)) else: z = z.basis_matrix() z = np.array([[I(e) for e in row] for row in z], dtype) X.append(np.array([[I(e) for e in row] for row in x], dtype)) Z.append(z) # Zero matrix x = copy(x) x[:] = F(0) z = x.row_space() if z.dimension() == 0: z = randint_matrix(0, 1, (0, deg)) else: z = z.basis_matrix() z = np.array([[I(e) for e in row] for row in z], dtype) X.append(np.array([[I(e) for e in row] for row in x], dtype)) Z.append(z) d = {"X": X, "Z": Z} save_pickle(d, folder, "row_space.pkl") set_seed(seed + 209) shapes = [(2, 2), (2, 3), (2, 4), (3, 2), (4, 2), (3, 3)] X = [] Z = [] for i in range(len(shapes)): deg = shapes[i][0] # The degree of the vector space # Random matrix x = randint_matrix(0, order, shapes[i]) X.append(x) x = matrix(FIELD, [[F(e) for e in row] for row in x]) z = x.column_space() if z.dimension() == 0: z = randint_matrix(0, 1, (0, deg)) else: z = z.basis_matrix() z = np.array([[I(e) for e in row] for row in z], dtype) Z.append(z) # Reduce the column space by 1 by copying the 0th column to the jth column for j in range(1, shapes[i][1]): x = copy(x) x[:, j] = F(random.randint(0, order - 1)) * x[:, 0] z = x.column_space() if z.dimension() == 0: z = randint_matrix(0, 1, (0, deg)) else: z = z.basis_matrix() z = np.array([[I(e) for e in row] for row in z], dtype) X.append(np.array([[I(e) for e in row] for row in x], dtype)) Z.append(z) # Zero matrix x = copy(x) x[:] = F(0) z = x.column_space() if z.dimension() == 0: z = randint_matrix(0, 1, (0, deg)) else: z = z.basis_matrix() z = np.array([[I(e) for e in row] for row in z], dtype) X.append(np.array([[I(e) for e in row] for row in x], dtype)) Z.append(z) d = {"X": X, "Z": Z} save_pickle(d, folder, "column_space.pkl") set_seed(seed + 210) shapes = [(2, 2), (2, 3), (2, 4), (3, 2), (4, 2), (3, 3)] X = [] Z = [] for i in range(len(shapes)): deg = shapes[i][0] # The degree of the vector space # Random matrix x = randint_matrix(0, order, shapes[i]) X.append(x) x = matrix(FIELD, [[F(e) for e in row] for row in x]) z = x.left_kernel() if z.dimension() == 0: z = randint_matrix(0, 1, (0, deg)) else: z = z.basis_matrix() z = np.array([[I(e) for e in row] for row in z], dtype) Z.append(z) # Reduce the left null space by 1 by copying the 0th row to the jth row for j in range(1, shapes[i][0]): x = copy(x) x[j, :] = F(random.randint(0, order - 1)) * x[0, :] z = x.left_kernel() if z.dimension() == 0: z = randint_matrix(0, 1, (0, deg)) else: z = z.basis_matrix() z = np.array([[I(e) for e in row] for row in z], dtype) X.append(np.array([[I(e) for e in row] for row in x], dtype)) Z.append(z) # Zero matrix x = copy(x) x[:] = F(0) z = x.left_kernel() if z.dimension() == 0: z = randint_matrix(0, 1, (0, deg)) else: z = z.basis_matrix() z = np.array([[I(e) for e in row] for row in z], dtype) X.append(np.array([[I(e) for e in row] for row in x], dtype)) Z.append(z) d = {"X": X, "Z": Z} save_pickle(d, folder, "left_null_space.pkl") set_seed(seed + 211) shapes = [(2, 2), (2, 3), (2, 4), (3, 2), (4, 2), (3, 3)] X = [] Z = [] for i in range(len(shapes)): deg = shapes[i][1] # The degree of the vector space # Random matrix x = randint_matrix(0, order, shapes[i]) X.append(x) x = matrix(FIELD, [[F(e) for e in row] for row in x]) z = x.right_kernel() if z.dimension() == 0: z = randint_matrix(0, 1, (0, deg)) else: z = z.basis_matrix() z = np.array([[I(e) for e in row] for row in z], dtype) Z.append(z) # Reduce the null space by 1 by copying the 0th column to the jth column for j in range(1, shapes[i][1]): x = copy(x) x[:, j] = F(random.randint(0, order - 1)) * x[:, 0] z = x.right_kernel() if z.dimension() == 0: z = randint_matrix(0, 1, (0, deg)) else: z = z.basis_matrix() z = np.array([[I(e) for e in row] for row in z], dtype) X.append(np.array([[I(e) for e in row] for row in x], dtype)) Z.append(z) # Zero matrix x = copy(x) x[:] = F(0) z = x.right_kernel() if z.dimension() == 0: z = randint_matrix(0, 1, (0, deg)) else: z = z.basis_matrix() z = np.array([[I(e) for e in row] for row in z], dtype) X.append(np.array([[I(e) for e in row] for row in x], dtype)) Z.append(z) d = {"X": X, "Z": Z} save_pickle(d, folder, "null_space.pkl") ############################################################################### # Polynomial arithmetic ############################################################################### folder = os.path.join(PATH, "polys", "data", sub_folder) if os.path.exists(folder): shutil.rmtree(folder) os.mkdir(folder) MIN_COEFFS = 1 MAX_COEFFS = 12 set_seed(seed + 101) X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] Y = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] Z = [] for i in range(len(X)): x = list_to_poly(X[i]) y = list_to_poly(Y[i]) z = x + y z = poly_to_list(z) Z.append(z) d = {"X": X, "Y": Y, "Z": Z} save_pickle(d, folder, "add.pkl") set_seed(seed + 102) X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] Y = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] Z = [] for i in range(len(X)): x = list_to_poly(X[i]) y = list_to_poly(Y[i]) z = x - y z = poly_to_list(z) Z.append(z) d = {"X": X, "Y": Y, "Z": Z} save_pickle(d, folder, "subtract.pkl") set_seed(seed + 103) X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] Y = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] Z = [] for i in range(len(X)): x = list_to_poly(X[i]) y = list_to_poly(Y[i]) z = x * y z = poly_to_list(z) Z.append(z) d = {"X": X, "Y": Y, "Z": Z} save_pickle(d, folder, "multiply.pkl") set_seed(seed + 104) X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] Y = [random.randint(1, 2 * characteristic) for i in range(20)] Z = [] for i in range(len(X)): x = list_to_poly(X[i]) y = Y[i] z = x * y z = poly_to_list(z) Z.append(z) d = {"X": X, "Y": Y, "Z": Z} save_pickle(d, folder, "scalar_multiply.pkl") set_seed(seed + 105) X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] Y = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] # Add some specific polynomial types X.append([0]), Y.append(random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS)) # 0 / y X.append(random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS // 2)), Y.append( random_coeffs(0, order, MAX_COEFFS // 2, MAX_COEFFS)) # x / y with x.degree < y.degree X.append(random_coeffs(0, order, 2, MAX_COEFFS)), Y.append( random_coeffs(0, order, 1, 2)) # x / y with y.degree = 0 Q = [] R = [] for i in range(len(X)): x = list_to_poly(X[i]) y = list_to_poly(Y[i]) q = x // y r = x % y q = poly_to_list(q) Q.append(q) r = poly_to_list(r) R.append(r) d = {"X": X, "Y": Y, "Q": Q, "R": R} save_pickle(d, folder, "divmod.pkl") set_seed(seed + 106) X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(4)] X.append(random_coeffs(0, order, 1, 2)) Y = [0, 1, 2, 3] Z = [] for i in range(len(X)): x = list_to_poly(X[i]) ZZ = [] for j in range(len(Y)): y = Y[j] z = x**y z = poly_to_list(z) ZZ.append(z) Z.append(ZZ) d = {"X": X, "Y": Y, "Z": Z} save_pickle(d, folder, "power.pkl") set_seed(seed + 107) X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] Y = arange(0, order, sparse=sparse) Z = np.array(np.zeros((len(X), len(Y))), dtype=dtype) for i in range(len(X)): for j in range(len(Y)): x = list_to_poly(X[i]) y = F(Y[j]) z = x(y) Z[i, j] = I(z) d = {"X": X, "Y": Y, "Z": Z} save_pickle(d, folder, "evaluate.pkl") set_seed(seed + 108) X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] Y = [randint_matrix(0, order, (2, 2)) for i in range(20)] Z = [] for i in range(len(X)): x = list_to_poly(X[i]) y = matrix(FIELD, [[F(e) for e in row] for row in Y[i]]) z = x(y) z = np.array([[I(e) for e in row] for row in z], dtype) Z.append(z) d = {"X": X, "Y": Y, "Z": Z} save_pickle(d, folder, "evaluate_matrix.pkl") ############################################################################### # Polynomial arithmetic methods ############################################################################### set_seed(seed + 301) X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] Z = [] for i in range(len(X)): x = list_to_poly(X[i]) z = x.reverse() z = poly_to_list(z) Z.append(z) d = {"X": X, "Z": Z} save_pickle(d, folder, "reverse.pkl") set_seed(seed + 302) X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] R = [] M = [] for i in range(len(X)): x = list_to_poly(X[i]) roots = x.roots() RR, MM = [], [] for root in roots: r = root[0] m = root[1] RR.append(I(r)) MM.append(int(m)) idxs = np.argsort(RR) # Sort by ascending roots RR = (np.array(RR, dtype=dtype)[idxs]).tolist() MM = (np.array(MM, dtype=dtype)[idxs]).tolist() R.append(RR) M.append(MM) d = {"X": X, "R": R, "M": M} save_pickle(d, folder, "roots.pkl") set_seed(seed + 303) X = [ random_coeffs(0, order, 2 * FIELD.degree(), 6 * FIELD.degree()) for i in range(20) ] Y = [ 1, ] * 10 + [random.randint(2, FIELD.degree() + 1) for i in range(10)] Z = [] for i in range(len(X)): x = list_to_poly(X[i]) z = x.derivative(Y[i]) z = poly_to_list(z) Z.append(z) d = {"X": X, "Y": Y, "Z": Z} save_pickle(d, folder, "derivative.pkl") ############################################################################### # Polynomial arithmetic functions ############################################################################### set_seed(seed + 401) X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] Y = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] D = [] S = [] T = [] for i in range(len(X)): x = list_to_poly(X[i]) y = list_to_poly(Y[i]) d, s, t = xgcd(x, y) d = poly_to_list(d) s = poly_to_list(s) t = poly_to_list(t) D.append(d) S.append(s) T.append(t) d = {"X": X, "Y": Y, "D": D, "S": S, "T": T} save_pickle(d, folder, "egcd.pkl") set_seed(seed + 402) X = [] Z = [] for i in range(20): XX = [ random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(random.randint(2, 5)) ] X.append(XX) xx = [list_to_poly(XXi) for XXi in XX] z = lcm(xx) z = poly_to_list(z) Z.append(z) d = {"X": X, "Z": Z} save_pickle(d, folder, "lcm.pkl") set_seed(seed + 403) X = [] Z = [] for i in range(20): XX = [ random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(random.randint(2, 5)) ] X.append(XX) xx = [list_to_poly(XXi) for XXi in XX] z = prod(xx) z = poly_to_list(z) Z.append(z) d = {"X": X, "Z": Z} save_pickle(d, folder, "prod.pkl") set_seed(seed + 404) X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] E = [random.randint(2, 10) for i in range(20)] M = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)] Z = [] for i in range(20): x = list_to_poly(X[i]) e = E[i] m = list_to_poly(M[i]) z = (x**e) % m z = poly_to_list(z) Z.append(z) d = {"X": X, "E": E, "M": M, "Z": Z} save_pickle(d, folder, "modular_power.pkl") set_seed(seed + 405) X = [ 0, ] * 20 # The remainder Y = [ 0, ] * 20 # The modulus Z = [ 0, ] * 20 # The solution for i in range(20): n = random.randint(2, 4) # The number of polynomials x, y = [], [] for j in range(n): d = random.randint(3, 5) x.append(random_coeffs(0, order, d, d + 1)) y.append(random_coeffs( 0, order, d + 1, d + 2)) # Ensure modulus degree is greater than remainder degree X[i] = x Y[i] = y try: x = [list_to_poly(xx) for xx in x] y = [list_to_poly(yy) for yy in y] z = crt(x, y) Z[i] = poly_to_list(z) except: Z[i] = None d = {"X": X, "Y": Y, "Z": Z} save_pickle(d, folder, "crt.pkl") ############################################################################### # Special polynomials ############################################################################### set_seed(seed + 501) X = [random_coeffs(0, order, 1, 6) for _ in range(20)] Z = [ False, ] * len(X) for i in range(len(X)): if random.choice(["one", "other"]) == "one": X[i][0] = 1 x = list_to_poly(X[i]) z = x.is_monic() Z[i] = bool(z) d = {"X": X, "Z": Z} save_pickle(d, folder, "is_monic.pkl") set_seed(seed + 502) IS = [] IS_NOT = [] if order <= 2**16: while len(IS) < 10: x = random_coeffs(0, order, 1, 6) f = list_to_poly(x) if f.is_irreducible(): IS.append(x) while len(IS_NOT) < 10: x = random_coeffs(0, order, 1, 6) f = list_to_poly(x) if not f.is_irreducible(): IS_NOT.append(x) d = {"IS": IS, "IS_NOT": IS_NOT} save_pickle(d, folder, "is_irreducible.pkl") set_seed(seed + 503) IS = [] IS_NOT = [] if order <= 2**16: while len(IS) < 10: x = random_coeffs(0, order, 1, 6) f = list_to_poly(x) # f = f / f.coefficients()[-1] # Make monic # assert f.is_monic() if f.degree() == 1 and f.coefficients(sparse=False)[0] == 0: continue # For some reason `is_primitive()` crashes on f(x) = a*x if not f.is_irreducible(): continue # Want to find an irreducible polynomial that is also primitive if f.is_primitive(): IS.append(x) while len(IS_NOT) < 10: x = random_coeffs(0, order, 1, 6) f = list_to_poly(x) # f = f / f.coefficients()[-1] # Make monic # assert f.is_monic() if f.degree() == 1 and f.coefficients(sparse=False)[0] == 0: continue # For some reason `is_primitive()` crashes on f(x) = a*x if not f.is_irreducible(): continue # Want to find an irreducible polynomial that is not primitive if not f.is_primitive(): IS_NOT.append(x) d = {"IS": IS, "IS_NOT": IS_NOT} save_pickle(d, folder, "is_primitive.pkl") set_seed(seed + 504) if order <= 2**16: X = [random_coeffs(0, order, 1, 6) for _ in range(20)] Z = [ False, ] * len(X) for i in range(len(X)): x = list_to_poly(X[i]) z = x.is_squarefree() Z[i] = bool(z) else: X = [] Z = [] d = {"X": X, "Z": Z} save_pickle(d, folder, "is_square_free.pkl")
def crack_by_pq(self): mp, mq = self.crack_by_p(), self.crack_by_q() return crt([mp, mq], [self.p, self.q])
def _faulty_sign(m, p, q, d_p, d_q): s_p = pow(m, d_p, p) s_q = pow(m, d_q, q) s_q ^= 1 return crt([s_p, s_q], [p, q])
def test_SECCON_2020_crypto01(): masked_flag = 8077275413285507538357977814283814136815067070436948406142717872478737670143034266458000767181162602217137657160614964831459653429727469965712631294123225 ciphertexts = [ ( 886775008733978973740257525650074677865489026053222554158847150065839924739525729402395428639350660027796013999414358689067171124475540042281892825140355436902197270317502862419355737833115120643020255177020178331283541002427279377648285229476212651491301857891044943211950307475969543789857568915505152189, 428559018841948586040450870835405618958247103990365896475476359721456520823105336402250177610460462882418373365551361955769011150860740050608377302711322189733933012507453380208960247649622047461113987220437673085, (80103920082434941308951713928956232093682985133090231319482222058601362901404235742975739345738195056858602864819514638254604213654261535503537998613664606957411824998071339098079622119781772477940471338367084695408560473046701072890754255641388442248683562485936267601216704036749070688609079527182189924, 842529568002033827493313169405480104392127700904794758022297608679141466431472390397211660887148306350328312067659313538964875094877785244888004725864621826211254824064492961341512012172365417791553455922872091302295614295785447354268009914265614957287377743897340475899980395298019735631787570231395791009 ), 59051335744243522933765175665, ), ( 37244493713246153778174562251362609960152354778990433088693945121840521598316370898923829767722653817418453309557995775960963654893571162621888675965423771020678918714406461976659339420718804301006282789714856197345257634581330970033427061846386874027391337895557558939538516456076874074642348992933600929747, 152657520846237173082171645969128953029734435220247551748055538422696193261367413610113664730705318574898280226247526051526753570012356026049784218573767765351341949785570284026342156807244268439356037529507501666987, (14301224815357080657295611483943004076662022528706782110580690613822599700117720290144067866898573981166927919045773324162651193822888938569692341428439887892150361361566562459037438581637126808773605536449091609307652818862273375400935935851849153633881318435727224452416837785155763820052159016539063161774, 711567521767597846126014317418558888899966530302382481896965868976010995445213619798358362850235642988939870722858624888544954189591439381230859036120045216842190726357421800117080884618285875510251442474167884705409694074544449959388473647082485564659040849556130494057742499029963072560315800049629531101 ), 56178670950277431873900982569, ), ( 6331516792334912993168705942035497262087604457828016897033541606942408964421467661323530702416001851055818803331278149668787143629857676822265398153269496232656278975610802921303671791426728632525217213666490425139054750899596417296214549901614709644307007461708968510489409278966392105040423902797155302293, 2041454339352622193656627164408774503102680941657965640324880658919242765901541504713444825283928928662755298871656269360429687747026596290805541345773049732439830112665647963627438433525577186905830365395002284129, (4957181230366693742871089608567285130576943723414681430592899115510633732100117146460557849481224254840925101767808247789524040371495334272723624632991086495766920694854539353934108291010560628236400352109307607758762704896162926316651462302829906095279281186440520100069819712951163378589378112311816255944, 2715356151156550887523953822376791368905549782137733960800063674240100539578667744855739741955125966795181973779300950371154326834354436541128751075733334790425302253483346802547763927140263874521376312837536602237535819978061120675338002761853910905172273772708894687214799261210445915799607932569795429868 ), 70953285682097151767648136059, ), ] HINTSIZE = 96 def decrypt(c, d, n): n = int(n) size = n.bit_length() // 2 c_high, c_low = c b = (c_low**2 - c_high**3) % n EC = EllipticCurve(Zmod(n), [0, b]) m_high, m_low = (EC((c_high, c_low)) * d).xy() m_high, m_low = int(m_high), int(m_low) return (m_high << size) | m_low hintmod = 2**HINTSIZE todec = masked_flag for data in ciphertexts: n, e, c, hint = data c_high, c_low = c for f in (e / QQ(n)).continued_fraction().convergents(): y = f.numerator() x = f.denominator() if is_prime(x) and is_prime(y): print("good", x, y) break plo = hint qlo = n * inverse_mod(plo, hintmod) % hintmod slo = (plo + qlo) % hintmod assert plo * qlo % hintmod == n % hintmod a = e * x - (n + 1) * y z_mod_y = (-a) % y z_mod_hintmod = (-a + slo * y) % hintmod midmod = hintmod * y zmid = crt([z_mod_y, z_mod_hintmod], [y, hintmod]) aa = a - slo * y + zmid assert aa % midmod == 0 aa //= midmod # shi = (p + q) // hintmod # zhi is ~216 bits # assert shi == aa + zhi sumpq = aa * hintmod eps = 2**216 * hintmod rsa = RSA(n) pbound, qbound = rsa.bound_primes_from_approximate_sum( sumpq - eps, 2 * eps) try: print("factor1") p, q = rsa.factor_with_prime_hint(low_mod=(plo, hintmod), bounds=pbound, beta=0.49) except RuntimeError: print("factor2") p, q = rsa.factor_with_prime_hint(low_mod=(qlo, hintmod), bounds=pbound, beta=0.49) print("ok!") d = inverse_mod(e, n + p + q + 1) mask = decrypt(c, d, n) todec ^= mask assert Bin(todec).bytes == b'SECCON{gut_poweeeeeeeeeeeeeer}'