def extract_basis(v, N): """Returns a subset of v such that the span of these elements is at least as big as v. In particular, if v is a vector space, it returns a base. """ dim = rank_of_vector_set(v, N) i = 0 basis = [] while i < len(v) and v[i] == 0: i += 1 if i == len(v): return [] basis.append(v[i]) if dim == 1: return basis i += 1 r = Matrix(GF(2), 1, N, [tobin(x, N) for x in basis]).rank() while r < dim and i < len(v): new_basis = basis + [v[i]] new_r = Matrix(GF(2), len(new_basis), N, [tobin(x, N) for x in new_basis]).rank() if new_r == dim: return new_basis elif new_r > r: basis = new_basis r = new_r i += 1 return []
def poly_to_set(E, v): q = E.base_field().order() deg = v.degree() e = 1 R = PolynomialRing(GF(q), 'x') f = R(v) roots = f.roots() while len(roots) != deg: e += 1 s = q**e R = PolynomialRing(GF(s), 'x') f = R(v) roots = f.roots() roots = [r[0] for r in roots] ext_E = extend_field(E, e) ee = 1 while True: Y = False for c in roots: if not ext_E.is_x_coord(c): Y = True break if Y: ee += 1 s = ee * e R = PolynomialRing(GF(q**(s)), 'x') f = R(v) roots = [r[0] for r in f.roots()] ext_E = extend_field(E, s) else: break return roots, ext_E
def encap(cls, pk, seed=None): """IND-CCA encapsulation sans compression or extra hash :param cls: Kyber class, inherit and change constants to change defaults :param pk: public key :param seed: seed used for random sampling if provided .. note :: Resembles Algorithm 4 of the Kyber paper. """ n = cls.n if seed is not None: set_random_seed(seed) m = random_vector(GF(2), n) m.set_immutable() set_random_seed(hash(m)) # NOTE: this is obviously not faithful K_ = random_vector(GF(2), n) K_.set_immutable() r = ZZ.random_element(0, 2**n-1) c = cls.enc(pk, m, r) K = hash((K_, c)) # NOTE: this obviously isn't a cryptographic hash return c, K
def tu_decomposition(s, v, verbose=False): """Using the knowledge that v is a subspace of Z_s of dimension n, a TU-decomposition (as defined in [Perrin17]) of s is performed and T and U are returned. """ N = int(log(len(s), 2)) # building B basis = extract_basis(v[0], N) t = len(basis) basis = complete_basis(basis, N) B = Matrix(GF(2), N, N, [tobin(x, N) for x in reversed(basis)]) if verbose: print "B= (rank={})\n{}".format(B.rank(), B.str()) # building A basis = complete_basis(extract_basis(v[1], N), N) A = Matrix(GF(2), N, N, [tobin(x, N) for x in reversed(basis)]) if verbose: print "A= (rank={})\n{}".format(A.rank(), A.str()) # building linear equivalent s_prime s_prime = [ apply_bin_mat(s[apply_bin_mat(x, A.inverse())], B) for x in xrange(0, 2**N) ] # TU decomposition of s' T, U = get_tu_open(s_prime, t) if verbose: print "T=[" for i in xrange(0, 2**(N - t)): print " {} {}".format(T[i], is_permutation(T[i])) print "]\nU=[" for i in xrange(0, 2**t): print " {} {}".format(U[i], is_permutation(U[i])) print "]" return T, U
def test_diagonal_locations(self): # tests the helper function `diagonal_locations` which identifies where # the diagonal elements or blocks occur in the H matrix H = Matrix(GF(2), [[0, 0, 0], [0, 0, 0], [0, 0, 0]]) index_one, index_B = diagonal_locations(H) self.assertEqual(index_one, 3) self.assertEqual(index_B, -1) H = Matrix(GF(2), [[1, 0, 0], [0, 0, 0], [0, 0, 0]]) index_one, index_B = diagonal_locations(H) self.assertEqual(index_one, 0) self.assertEqual(index_B, -1) H = Matrix(GF(2), [[0, 1, 0], [1, 0, 0], [0, 0, 0]]) index_one, index_B = diagonal_locations(H) self.assertEqual(index_one, 3) self.assertEqual(index_B, 0) H = Matrix(GF(2), [[0, 1, 0], [1, 0, 0], [0, 0, 1]]) index_one, index_B = diagonal_locations(H) self.assertEqual(index_one, 2) self.assertEqual(index_B, 0) H = Matrix(GF(2), [[1, 0, 0], [0, 0, 1], [0, 1, 0]]) index_one, index_B = diagonal_locations(H) self.assertEqual(index_one, 0) self.assertEqual(index_B, 1)
def make_base_field(self): if self.args.base_field != None: F = GF(self.args.base_field, 'F') self.field_traits.check(F) return F p = next_prime(2**self.args.num_bits) while not self.field_traits.check(GF(p, 'F'), nothrow=True): p = next_prime(p) return GF(p, 'F')
def attack(p, x1, y1, x2, y2): """ Recovers the a and b parameters from an elliptic curve when two points are known. :param p: the prime of the curve base ring :param x1: the x coordinate of the first point :param y1: the y coordinate of the first point :param x2: the x coordinate of the second point :param y2: the y coordinate of the second point :return: a tuple containing the a and b parameters of the elliptic curve """ m = Matrix(GF(p), [[x1, 1], [x2, 1]]) v = vector(GF(p), [y1**2 - x1**3, y2**2 - x2**3]) a, b = m.solve_right(v) return int(a), int(b)
def test_skipper_cpa_enc(skipper=Skipper4, kyber=Kyber, t=128, l=None, exhaustive=False): if not exhaustive: for i in range(t): pk, sk = kyber.key_gen(seed=i) m0 = random_vector(GF(2), kyber.n) c = skipper.enc(kyber, pk, m0, seed=i, l=l) m1 = kyber.dec(sk, c) assert(m0 == m1) else: # exhaustive test for i in range(16): pk, sk = kyber.key_gen(seed=i) for m0 in FreeModule(GF(2), kyber.n): c = skipper.enc(kyber, pk, m0, seed=i, l=l) m1 = kyber.dec(sk, c) assert(m0 == m1)
def irreps(p, q): """ Returns the irreducible representations of the cyclic group C_p over the field F_q, where p and q are distinct primes. Each representation is given by a matrix over F_q giving the action of the preferred generator of C_p. sage: [M.nrows() for M in irreps(3, 7)] [1, 1, 1] sage: [M.nrows() for M in irreps(7, 11)] [1, 3, 3] sage: sum(M.nrows() for M in irreps(157, 13)) 157 """ p, q = ZZ(p), ZZ(q) assert p.is_prime() and q.is_prime() and p != q R = PolynomialRing(GF(q), 'x') x = R.gen() polys = [f for f, e in (x**p - 1).factor()] polys.sort(key=lambda f: (f.degree(), -f.constant_coefficient())) reps = [poly_to_rep(f) for f in polys] assert all(A**p == 1 for A in reps) assert reps[0] == 1 return reps
def delta_rank(f): """Returns the Gamma-rank of the function with LUT f. The Gamma-rank is the rank of the 2^{2n} \times 2^{2n} binary matrix M defined by M[x][y] = 1 if and only if x + y \in \Delta, where \Delta is defined as \Delta = \{ (a, b), DDT_f[a][b] == 2 \} ~. """ n = int(log(len(f), 2)) dim = 2**(2 * n) d = ddt(f) gamma = [(a << n) | b for a, b in itertools.product(xrange(1, 2**n), xrange(0, 2**n)) if d[a][b] == 2] mat_content = [] for x in xrange(0, dim): row = [0 for j in xrange(0, dim)] for y in gamma: row[oplus(x, y)] = 1 mat_content.append(row) mat_gf2 = Matrix(GF(2), dim, dim, mat_content) return mat_gf2.rank()
def try_solve(lst): from sage.all import GF, MatrixSpace, VectorSpace, matrix A = [] B = [] NK = 32 R = GF(2) for uu in lst: A.append(bin2vec(uu[0], NK)) B.append(bin2vec(uu[1], NK)) A = matrix(R, A) print('rank is >> ', A.rank()) B = matrix(R, B) try: X = A.solve_right(B) except: return 0 print(A.str()) print() print(B.str()) print() print(X) print() print((A * X - B).str()) print() return 1
def make_reductions(nf, ell, verbose=False): """Input: A newform nf and a prime ell Output: a list of all the associated reducton maps Method: depends on the Hecke field. Trivial if it is QQ, otherwise construct the Hecke order's ZZ-basis (betas) depending on the type of basis stored in the newform. """ K = nf.hecke_field if verbose: print("Making reduction mod ell={}".format(ell)) if K==QQ: nf.betas = [[1]] # for consistency, not actually used Fl = GF(ell) return [Fl(1)] try: betas = nf.betas # we'll have them already if the data came from a file except AttributeError: if not nf.hecke_ring_numerators:#nf.hecke_ring_power_basis: a = K.gen() betas = [a**i for i in range(K.degree())] else: # generic betas = [K(c)/d for c,d in zip(nf.hecke_ring_numerators, nf.hecke_ring_denominators)] nf.betas = betas return reduction_maps(betas, ell, verbose)
def apoly(manifold, rational_coeff=False, method='sage'): """ Computes the SL(2, C) version of the A-polynomial starting from the extended Ptolemy variety. By default, uses Sage (which is to say Singular) to eliminate variables. Surprisingly, Macaulay2 is *much* slower. sage: M = Manifold('m003') sage: I = apoly(M) sage: I.gens() [M^4*L^2 + M^3*L^2 - M*L^4 - 2*M^2*L^2 - M^3 + M*L^2 + L^2] """ I = extended_ptolemy_equations(manifold) R = I.ring() if rational_coeff == False: F = GF(31991) R = R.change_ring(F) I = I.change_ring(R) to_elim = [R(x) for x in R.variable_names() if x not in ['M', 'L']] if method == 'sage': return I.elimination_ideal(to_elim) elif method == 'M2': from sage.all import macaulay2 I_m2 = macaulay2(I) return I_m2.eliminate('{' + repr(to_elim)[1:-1] + '}').to_sage() else: raise ValueError("method flag should be in ['sage', 'M2']")
def test_semi_stable_frobenius_polynomial(): # test that we indeed have a 73 isogeny mod p for p in 2, 3, 5, 7, 11, 19: for pp, e in (p * K).factor(): f = semi_stable_frobenius_polynomial(E, pp) assert not f.change_ring(GF(73)).is_irreducible()
def decap(cls, sk, pk, c): """IND-CCA decapsulation :param cls: Kyber class, inherit and change constants to change defaults :param sk: secret key :param pk: public key :param c: ciphertext .. note :: Resembles Algorithm 5 of the Kyber paper. """ n = cls.n m = cls.dec(sk, c) m.set_immutable() set_random_seed(hash(m)) # NOTE: this is obviously not faithful K_ = random_vector(GF(2), n) K_.set_immutable() r = ZZ.random_element(0, 2**n-1) c_ = cls.enc(pk, m, r) if c == c_: return hash((K_, c)) # NOTE: this obviously isn't a cryptographic hash else: return hash(c) # NOTE ignoring z
def generate_reference_columns(k): global eqn_row, result_dictionary, reference_dictionary, num_vars, iteration_size b = map_enumerations(map_subset(bsets[0])) for aset in asets: a = map_enumerations(map_subset(aset)) if matrix(GF(p[0]), a + b, sparse=False).rank() < len(p): continue # j = enumerate_permutation(a+b) eqn_row = eqn_row + 1 iteration_size = iteration_size + 1 # result_dictionary[(j, eqn_row)] = 1 # for i in range(k): # a_generated = [vector_mod_subtract(a[i1], a[i], p) for i1 in range(k)] # a_generated[i] = a[i] # calculate_column_values(a_generated, b, k, i, a) # reference_dictionary = {} # items = sorted(result_dictionary.items()) # for key, val in items: # if val != 0: # column, row = key # # print(row,column, val) # if column in reference_dictionary: # reference_dictionary[column].append((row,val)) # else: # reference_dictionary[column] = [(row,val)] # print(reference_dictionary) result_dictionary = {} eqn_row = 0
def is_independent(self, v): """ Return True if the Hecke operators in v are independent. INPUT: - `v` -- four elements of the Hecke algebra mod 2 (represented as matrices) OUTPUT: - bool EXAMPLES:: sage: from mdsage import * sage: C = KamiennyCriterion(29) sage: C.is_independent([C.T(1), C.T(2), C.T(3), C.T(4)]) True sage: C.is_independent([C.T(1), C.T(2), C.T(3), C.T(1)+C.T(3)]) False """ # X = matrix(GF(2), 4, sum([a.list() for a in v], [])) # c = sage.matrix.matrix_modn_dense.Matrix_modn_dense(X.parent(),X.list(),False,True) # return c.rank() == 4 # This crashes! See http://trac.sagemath.org/sage_trac/ticket/8301 return matrix(GF(2), len(v), sum([a.list() for a in v], [])).rank() == len(v) raise NotImplementedError
def key_gen(cls, seed=None): """Generate a new public/secret key pair :param cls: Kyber class, inherit and change constants to change defaults :param seed: seed used for random sampling if provided .. note :: Resembles Algorithm 1 of the Kyber paper. """ n, q, eta, k, D = cls.n, cls.q, cls.eta, cls.k, cls.D if seed is not None: set_random_seed(seed) R, x = PolynomialRing(ZZ, "x").objgen() Rq = PolynomialRing(GF(q), "x") f = R(cls.f) A = matrix(Rq, k, k, [Rq.random_element(degree=n - 1) for _ in range(k * k)]) s = vector(R, k, [R([(D(eta)) for _ in range(n)]) for _ in range(k)]) e = vector(R, k, [R([(D(eta)) for _ in range(n)]) for _ in range(k)]) t = (A * s + e) % f # NOTE ignoring compression return (A, t), s
def integral_basis_of_unramified_subfield(self, precision=5): r""" Return an (approximate) integral basis of the maximal unramified subfield. INPUT: - ``precison`` -- a positive integer OUTPUT: A list `\alpha=(\alpha_0,\ldots,\alpha_{m-1})` of elements of `K_0` which approximate (with precision `p^N` an integral basis of the maximal unramified subfield. """ if not hasattr(self, "_integral_basis_of_unramified_subfield") or precision > 5: m = self.inertia_degree() if m == 1: return [QQ.one()] p = self.p() fb = GF(p**m, 'zeta').polynomial() f = fb.change_ring(self.number_field()) fx = f.derivative() vK = self.valuation() k = vK.residue_field() zetab = fb.change_ring(k).roots()[0][0] assert fb(zetab) == 0 zeta = vK.lift(zetab) while vK(f(zeta)) <= precision: zeta = zeta - f(zeta)/fx(zeta) zeta = self.reduce(zeta, precision +1) # now zeta is an approximate generator of the maximal unramified subfield self._integral_basis_of_unramified_subfield = [self.reduce(zeta**i, precision +1) for i in range(m)] return self._integral_basis_of_unramified_subfield
def iterate_permutations(k): global eqn_row, result_string, array_result # bigger than required array array_result = [[] for i in range((len(asets) * len(bsets)) + 1)] # iteration_chunk = iteration_size # eqn_chunk = 0 for bset in bsets: b = map_enumerations(map_subset(bset)) # initial_row = iteration_chunk*eqn_chunk for aset in asets: a = map_enumerations(map_subset(aset)) if matrix(GF(p[0]), a + b, sparse=False).rank() < len(p): continue # print(a+b) eqn_row = eqn_row + 1 j = enumerate_permutation(a + b) result_dictionary[(eqn_row, j)] = 1 for i in range(k): a_generated = [ vector_mod_subtract(a[i1], a[i], p) for i1 in range(k) ] a_generated[i] = a[i] # print(a_generated) # calculate_column_values(a_generated, b, k, i, a) calculate_column_values_dynamic(a_generated, b, i)
def challenge(): # Define the curve p = 0xA15C4FB663A578D8B2496D3151A946119EE42695E18E13E90600192B1D0ABDBB6F787F90C8D102FF88E284DD4526F5F6B6C980BF88F1D0490714B67E8A2A2B77 a = 0x5E009506FCC7EFF573BC960D88638FE25E76A9B6C7CAEEA072A27DCD1FA46ABB15B7B6210CF90CABA982893EE2779669BAC06E267013486B22FF3E24ABAE2D42 b = 0x2CE7D1CA4493B0977F088F6D30D9241F8048FDEA112CC385B793BCE953998CAAE680864A7D3AA437EA3FFD1441CA3FB352B0B710BB3F053E980E503BE9A7FECE E = EllipticCurve(GF(p), [a, b]) # generator's public key G = E.point(( 0x39F15E024D44228FD11C58A71C312FD64167C7D249D5677DA0DFB4B9C3ED0F90701837A5E77B5A2B74433D7FBE027CD0C73B5AA7B300F7384521AF0DC283DC6D, 0x5F3636A89167A6FBB7B7D1AD97D11C70756835B5F1273E20C06D9E022D74648EC22A3F92C378196D137C3F2027A67381BE79E1C0D65CD9B61211A77A3842C8B2, )) # alice's public key A = E.point(( 0x5AA8B5CF3124C552881BA67C14C863BB2FF30D960FE41B61123D2025CDDDF0EA75E92D76326BE9FB09B9A32373FC278AC8D5CF5CA83B9E517CE347C6879BEF51, 0x2E3DDEC1B35930B1039351B2814252186B30CE27CE15EDA4351428868AE8593AB8C61C034BA10041CCE205D7F7102C292F30AC5F3D2A2C2F3A463D837DF070CD, )) # bob's public key B = E.point(( 0x7F0489E4EFE6905F039476DB54F9B6EAC654C780342169155344ABC5AC90167ADC6B8DABACEC643CBE420ABFFE9760CBC3E8A2B508D24779461C19B20E242A38, 0xDD04134E747354E5B9618D8CB3F60E03A74A709D4956641B234DAA8A65D43DF34E18D00A59C070801178D198E8905EF670118C15B0906D3A00A662D3A2736BF, )) # Ciphertext iv = "719700b2470525781cc844db1febd994" encrypted_flag = "335470f413c225b705db2e930b9d460d3947b3836059fb890b044e46cbb343f0" n = smart_attack(E, G, A, p, a, b) secret = (n * B).xy()[0] print(decrypt_flag(secret, iv, encrypted_flag))
def _to_relaxed_matrix_bp(self, graph): # convert graph to a topological sorting of the vertices nodes = nx.topological_sort(graph.graph) # the accept vertex must be last a = nodes.index(('acc', graph.num)) b = nodes.index(('rej', graph.num)) if a < b: nodes[b], nodes[a] = nodes[a], nodes[b] # the source vertex must be first src = nodes.index(('src', graph.num)) nodes[0], nodes[src] = nodes[src], nodes[0] mapping = dict(zip(nodes, range(self.size))) g = nx.relabel_nodes(graph.graph, mapping) # convert graph to relaxed matrix BP self.bp = [] G = MatrixSpace(GF(2), self.size) for layer in xrange(self.nlayers): zero = copy(G.one()) one = copy(G.one()) for edge in g.edges_iter(): e = g[edge[0]][edge[1]] assert e['label'] in (0, 1) if g.node[edge[0]]['layer'] == layer: if e['label'] == 0: zero[edge[0], edge[1]] = 1 else: one[edge[0], edge[1]] = 1 self.bp.append(Layer(graph.inp(layer), zero, one)) self.zero = G.one()
def diagonal_locations(H): r"""Returns the indices of the last `1` along the diagonal and the first block along the diagonal of `H`. Parameters ---------- H : symmetric GF(2) matrix Contains either 1's along the diagonal or anti-symmetric blocks. Returns ------- index_one : integer The last occurrence of a `1` along the diagonal of `H`. Equal to `g` if there are no ones along the diagonal. index_B : integer The first occurrence of a block along the diagonal of `H`. Equal to `-1` if there are no blocks along the diagonal. """ g = H.nrows() B = Matrix(GF(2),[[0,1],[1,0]]) try: index_one = min(j for j in range(g) if H[j,j] == 1) except ValueError: index_one = g try: index_B = max(j for j in range(g-1) if H[j:(j+2),j:(j+2)] == B) except ValueError: index_B = -1 return index_one, index_B
def rank(self, field_size): """ Returns rank of u_mat U gamma_set """ rank = 0 # If nothing in gamma_set yet, use u_vector if len(self.gamma_set) == 0: for val in self.u_vector: if val != 0: rank += 1 return rank # Get Rank of C C = np.append(self.u_mat, self.gamma_set) C_list = [] if len(self.u_mat) == 0: for i in range(0, len(self.gamma_set)): C_list.append([]) else: for row in self.u_mat: C_list.append(row.tolist()) for i in range(0, len(self.gamma_set)): for j in range(0, len(self.gamma_set[i])): C_list[i].append(self.gamma_set[i][j]) C = matrix(GF(field_size), C_list) return C.rank()
def decode(m, q, n): """Decode vector `m` to `\{0,1\}^n` depending on distance to `q/2` :param m: a vector of length `\leq n` :param q: modulus """ return vector(GF(2), n, [abs(e)>q/ZZ(4) for e in m] + [0 for _ in range(n-len(m))])
def test_ea_permutations(): for N in [4, 5]: F = GF(2**N, name="a") inv = [(F.fetch_int(x)**(2**N - 2)).integer_representation() for x in xrange(0, 2**N)] print("== " + str(N)) for L in ea_equivalent_permutation_mappings(inv): print(L.str() + "\n")
def get_ccz_equivalent_permutation_cartesian(s, v0, v1, verbose=False): """Takes as input two vector spaces v0 and v1 of the set of zeroes in the LAT of s, each being the cartesian product of two spaces, and returns a permutation CCZ equivalent to s obtained using these CCZ spaces. """ N = int(log(len(s), 2)) # building B e1 = extract_basis(v0[0], N) t = len(e1) e2 = extract_basis(v1[0], N) B = Matrix(GF(2), N, N, [tobin(x, N) for x in e1 + e2]) if verbose: print "B= (rank={})\n{}".format(B.rank(), B.str()) # building A e1 = extract_basis(v0[1], N) e2 = extract_basis(v1[1], N) A = Matrix(GF(2), N, N, [tobin(x, N) for x in e1 + e2]) if verbose: print "A= (rank={})\n{}".format(A.rank(), A.str()) # building linear equivalent s_prime s_prime = [ apply_bin_mat(s[apply_bin_mat(x, A.inverse())], B) for x in xrange(0, 2**N) ] # TU decomposition of s' T_inv, U = get_tu_closed(s_prime, t) if verbose: print "T_inv=[" for i in xrange(0, 2**t): print " {} {}".format(T_inv[i], is_permutation(T_inv[i])) print "]\nU=[" for i in xrange(0, 2**t): print " {} {}".format(U[i], is_permutation(U[i])) print "]" # TU-unfolding T = [inverse(row) for row in T_inv] result = [0 for x in xrange(0, 2**N)] for l in xrange(0, 2**t): for r in xrange(0, 2**(N - t)): x = (l << (N - t)) | r y_r = T[r][l] y_l = U[y_r][r] result[x] = (y_l << (t)) | y_r return result
def matrix_from_masks(basis, N): """Returns an NxN binary matrix M such that M*(1 << i) = basis[i] for all i < len(basis). """ b = basis + [0] * (N - len(basis)) return Matrix(GF(2), N, N, [[(b[i] >> j) & 1 for j in reversed(range(0, N))] for i in range(0, N)]).transpose()
def F_2t_to_space(basis, n): """Returns the matrix corresponding to a permutation of (F_2)^n such that F_2^t (i.e. the set of integers < 2^t) is mapped to the space with the given basis using the function apply_bin_mat() """ full_basis = complete_basis(basis, n) return Matrix(GF(2), n, n, [[(v >> (n - 1 - j)) & 1 for j in xrange(0, n)] for v in reversed(full_basis)]).transpose()
def get_generating_matrix(basis, N): """Returns an NxN binary matrix M such that M*(1 << i) = basis[i] for all i < len(basis) and such that M has full rank. """ b = complete_basis(basis, N) return Matrix(GF(2), N, N, [[(b[i] >> j) & 1 for j in reversed(xrange(0, N))] for i in xrange(0, N)]).transpose()