def algebraic_normal_form_coordinate(s, i): """Returns the algebraic normal form of the `i`th coordinate of the SBox s. """ coordinate = BooleanFunction([(x >> i) & 1 for x in list(s)]) return coordinate.algebraic_normal_form()
def linear_approximation_matrix(self): """ Return linear approximation matrix ``A`` for this S-box. Let ``i_b`` be the ``b``-th bit of ``i`` and ``o_b`` the ``b``-th bit of ``o``. Then ``v = A[i,o]`` encodes the bias of the equation ``sum( i_b * x_i ) = sum( o_b * y_i )`` if ``x_i`` and ``y_i`` represent the input and output variables of the S-box. See [He2002]_ for an introduction to linear cryptanalysis. EXAMPLES:: sage: from sage.crypto.sbox import SBox sage: S = SBox(7,6,0,4,2,5,1,3) sage: S.linear_approximation_matrix() [ 4 0 0 0 0 0 0 0] [ 0 0 0 0 2 2 2 -2] [ 0 0 -2 -2 -2 2 0 0] [ 0 0 -2 2 0 0 -2 -2] [ 0 2 0 2 -2 0 2 0] [ 0 -2 0 2 0 2 0 2] [ 0 -2 -2 0 0 -2 2 0] [ 0 -2 2 0 -2 0 0 -2] According to this matrix the first bit of the input is equal to the third bit of the output 6 out of 8 times:: sage: for i in srange(8): print(S.to_bits(i)[0] == S.to_bits(S(i))[2]) False True True True False True True True """ m = self.m n = self.n nrows = 1<<m ncols = 1<<n B = BooleanFunction(self.m) L = [] for j in range(ncols): for i in range(nrows): B[i] = ZZ(self(i)&j).popcount() L.append(B.walsh_hadamard_transform()) A = Matrix(ZZ, ncols, nrows, L) A = -A.transpose()/2 A.set_immutable() return A
def linear_approximation_matrix(self): """ Return linear approximation matrix ``A`` for this S-box. Let ``i_b`` be the ``b``-th bit of ``i`` and ``o_b`` the ``b``-th bit of ``o``. Then ``v = A[i,o]`` encodes the bias of the equation ``sum( i_b * x_i ) = sum( o_b * y_i )`` if ``x_i`` and ``y_i`` represent the input and output variables of the S-box. See [He2002]_ for an introduction to linear cryptanalysis. EXAMPLES:: sage: from sage.crypto.sbox import SBox sage: S = SBox(7,6,0,4,2,5,1,3) sage: S.linear_approximation_matrix() [ 4 0 0 0 0 0 0 0] [ 0 0 0 0 2 2 2 -2] [ 0 0 -2 -2 -2 2 0 0] [ 0 0 -2 2 0 0 -2 -2] [ 0 2 0 2 -2 0 2 0] [ 0 -2 0 2 0 2 0 2] [ 0 -2 -2 0 0 -2 2 0] [ 0 -2 2 0 -2 0 0 -2] According to this matrix the first bit of the input is equal to the third bit of the output 6 out of 8 times:: sage: for i in srange(8): print(S.to_bits(i)[0] == S.to_bits(S(i))[2]) False True True True False True True True """ m = self.m n = self.n nrows = 1<<m ncols = 1<<n B = BooleanFunction(self.m) L = [] for j in range(ncols): for i in range(nrows): B[i] = ZZ(self(i)&j).popcount() L.append(B.walsh_hadamard_transform()) A = Matrix(ZZ, ncols, nrows, L) A = -A.transpose()/2 A.set_immutable() return A
def save_classifications_in_parallel(comm, name_prefix, list_of_f, start=0, stop=None, directory=None): r""" Using MPI, construct and save a number of Cayley graph classifications corresponding to a list of bent functions. INPUT: - ``comm`` -- MPI communicator. - ``name_prefix`` -- String. Name prefix to use with ``save_mangled`` to save each classification. - ``list_of_f`` -- List of forms or bent functions. - ``start`` -- Integer. Default=0. Index of start position in the list. - ``stop`` -- Integer. Default=None. Index after end position, or ``None`` if whole remaining list. - ``directory`` -- string, optional. The directory where the object is to be saved. Default is None, meaning the current directory. OUTPUT: None. EFFECT: Uses ``name`` to save the classifications corresponding to ``list_of_f``. """ rank = comm.Get_rank() size = comm.Get_size() if stop == None: stop = len(list_of_f) for n in range(start + rank, stop, size): name = name_prefix + '_' + str(n) form = BooleanFunction(list_of_f[n]).truth_table(format='hex') save_one_classification(name, form, directory=directory)
def component_function(self, b): r""" Return a Boolean function corresponding to the component function `b \cdot S(x)`. If `S` is an `m \times n` S-Box, then `b \in \GF{2}^n` and `\cdot` denotes dot product of two vectors. INPUT: - ``b`` -- either an integer or a tuple of `\GF{2}` elements of length ``self.n`` EXAMPLES:: sage: S = mq.SBox([7,6,0,4,2,5,1,3]) sage: f3 = S.component_function(3) sage: f3.algebraic_normal_form() x0*x1 + x0*x2 + x0 + x2 sage: f5 = S.component_function([1, 0, 1]) sage: f5.algebraic_normal_form() x0*x2 + x0 + x1*x2 """ m = self.m n = self.n ret = BooleanFunction(m) if isinstance(b, (int, long, Integer)): b = vector(GF(2), self.to_bits(b, n)) elif len(b) == n: b = vector(GF(2), b) else: raise TypeError( "cannot compute component function using parameter %s" % (b, )) for x in range(1 << m): ret[x] = bool( b.dot_product(vector(GF(2), self.to_bits(self(x), n)))) return ret
def __or__(self, other): """ Return the concatenation of `self` and `other` which must have the same number of variables. INPUT: - ``self`` -- the current object. - ``other`` -- another Boolean function. OUTPUT: The concatenation of `self`and `other` EXAMPLES: :: sage: from boolean_cayley_graphs.boolean_function_improved import BooleanFunctionImproved sage: bf0 = BooleanFunctionImproved([1,0,1,0]) sage: bf1 = BooleanFunctionImproved([1,1,0,0]) sage: (bf0|bf1).truth_table(format='int') (1, 0, 1, 0, 1, 1, 0, 0) sage: C = bf0.truth_table() + bf1.truth_table() sage: (bf0|bf1).truth_table(format='int') == C True TESTS: :: sage: bf0|BooleanFunctionImproved([0,1]) Traceback (most recent call last): ... ValueError: the two Boolean functions must have the same number of variables """ bf_self = BooleanFunction(self) return type(self)(bf_self | other)
def __mul__(self, other): """ Return the elementwise product of `self`and `other` which must have the same number of variables. INPUT: - ``self`` -- the current object. - ``other`` -- another Boolean function. OUTPUT: The elementwise product of `self`and `other` EXAMPLES: :: sage: from boolean_cayley_graphs.boolean_function_improved import BooleanFunctionImproved sage: bf0 = BooleanFunctionImproved([1,0,1,0]) sage: bf1 = BooleanFunctionImproved([1,1,0,0]) sage: (bf0*bf1).truth_table(format='int') (1, 0, 0, 0) sage: P = bf0.algebraic_normal_form() * bf1.algebraic_normal_form() sage: (bf0*bf1).algebraic_normal_form() == P True TESTS: :: sage: bf0*BooleanFunctionImproved([0,1]) Traceback (most recent call last): ... ValueError: the two Boolean functions must have the same number of variables """ bf_self = BooleanFunction(self) return type(self)(bf_self * other)
def __invert__(self): """ Return the complement Boolean function of `self`. INPUT: - ``self`` -- the current object. EXAMPLES: :: sage: from boolean_cayley_graphs.boolean_function_improved import BooleanFunctionImproved sage: bf0 = BooleanFunctionImproved([1,0,1,1]) sage: bf1 = ~bf0 sage: type(bf1) <class 'boolean_cayley_graphs.boolean_function_improved.BooleanFunctionImproved'> sage: bf1.algebraic_normal_form() x0*x1 + x0 sage: bf1.truth_table() (False, True, False, False) """ bf_self = BooleanFunction(self) return type(self)(~bf_self)
if(tt[i] == 1): print(i) #print(join_arr(get_cv(p))) f = open("input","r") arr = f.readlines() f.close() arr = arr[2:] print(arr) bfs = [] for i in range(0, len(arr)): print(len(arr[i][:-1])) bfs.append(BooleanFunction(arr[i][:-1]))
CYCLE_LENGTHS[i] = len(CYCLES[i]) if (CYCLE_LENGTHS[i] & _sage_const_0x1) != _sage_const_0: AT_LEAST_ONE_ODD_FLAG = _sage_const_1 print("The length of each cycle : "), (CYCLE_LENGTHS) #start computing _0_BASIS = range(NUM_CYCLES) for i in range(NUM_CYCLES): Boolean_Table = range(S_BOX_CARDINALITY) for j in range(S_BOX_CARDINALITY): if j in CYCLES[i]: Boolean_Table[j] = _sage_const_1 else: Boolean_Table[j] = _sage_const_0 _0_BASIS[i] = BooleanFunction(Boolean_Table) if is_nonlinear_invariant(S_BOX, _0_BASIS[i]) is not True: print("Warning! "), (_0_BASIS[i].algebraic_normal_form()), ( "is not a nonlinear invariant but it is got.") #S-Box(x(n-1), x(n-2), ..., x0) NUM_NON_INVARIANT = _sage_const_0 NUM_NON_INVARIANT_WITH_LINEAR_STRUCTURE = _sage_const_0 NUM_BALANCED_NON_INVARIANT_WITH_LINEAR_STRUCTURE = _sage_const_0 if AT_LEAST_ONE_ODD_FLAG == _sage_const_1: NUM_NON_INVARIANT = _sage_const_2**(NUM_CYCLES) else: NUM_NON_INVARIANT = _sage_const_2**(NUM_CYCLES + _sage_const_1) print("== Nonlinear Invariants Info of %s" % ALG_NAME)
# In[121]: nf = x0*x1*x2*x3*x4*x5 + x0*x1*x2*x3*x4 + x0*x1*x2*x3 + x0*x1*x2*x4 + x0*x1*x3*x4*x5 + x0*x1*x4*x5 + x0*x1*x4 + x0*x1*x5 + x0*x2*x3*x4 + x0*x2*x3*x5 + x0*x2*x3 + x0*x2*x4 + x0*x3*x4*x5 + x0*x3*x4 + x0*x3*x5 + x0*x3 + x0*x4 + x0*x5 + x0 + x1*x2*x3*x4*x5 + x1*x2*x3*x4 + x1*x2*x4 + x1*x2*x5 + x1*x3 + x1*x4*x5 + x1*x4 + x1*x5 + x1 + x2*x3*x4*x5 + x2*x3*x5 + x2*x3 + x2*x4*x5 + x2*x4 + x2*x5 + x2 + x3*x4*x5 + 1 # In[119]: from sage.crypto.boolean_function import BooleanFunction tb = [0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1] tb = [0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0] tb = [a^1 for a in tb] func = BooleanFunction(tb) # In[122]: f2 = BooleanFunction(nf) for a, b in zip(func.truth_table(), f2.truth_table()): print(a,b) # In[125]: print(func.algebraic_normal_form()) print(func.absolute_autocorrelation())
if S_BOX_CARDINALITY < ANALYSIS_BIT_RANGE: boolean = _sage_const_0x1 << S_BOX_CARDINALITY while boolean < (_sage_const_0x2 << S_BOX_CARDINALITY): boolean_table = [int(x) for x in bin(boolean)[_sage_const_3:]] minus_pow_boolean = boolean_to_minus_power(boolean_table) walsh_coeff = walsh_trasform(minus_pow_boolean) walsh_coeff_vec = vector(walsh_coeff) #Check if the walsh coefficient vector is the eigenvector of M with S_BOX_CARDINALITY or -S_BOX_CARDINALITY eigenvalues. if (walsh_coeff_vec in eigenspace_with_plus_size_value or walsh_coeff_vec in eigenspace_with_minus_size_value): NUM_NON_INVARIANT += _sage_const_1 #Check if the nonlinear invariant has the linear structures. if BooleanFunction( boolean_table).has_linear_structure() == _sage_const_1: NUM_NON_INVARIANT_WITH_LINEAR_STRUCTURE += _sage_const_1 #Check if the nonlinear invariant is balanced. if BooleanFunction( boolean_table).is_balanced() == _sage_const_1: NUM_BALANCED_NON_INVARIANT_WITH_LINEAR_STRUCTURE += _sage_const_1 boolean += _sage_const_1 print("# of Nonlinear Invariants : %d" % NUM_NON_INVARIANT) print("# of Nonlinear Invariants with Linear Structure : %d" % NUM_NON_INVARIANT_WITH_LINEAR_STRUCTURE) print("# of Balanced Nonlinear Invariants with Linear Structure : %d" % NUM_BALANCED_NON_INVARIANT_WITH_LINEAR_STRUCTURE) else: print("# of nonlinear invariants is too large to analyze.")
[x15, x14, x13, x12, x11, x10, x9, x8, x7, x6, x5, x4, x3, x2, x1, x0], [ _sage_const_2, _sage_const_2, _sage_const_2, _sage_const_2, _sage_const_2, _sage_const_2, _sage_const_2, _sage_const_2, _sage_const_2, _sage_const_2, _sage_const_2, _sage_const_2, _sage_const_2, _sage_const_2, _sage_const_2, _sage_const_2 ]) else: print("The bit size of the S-box is not supported") exit NUM_MONOMIALS = int(_sage_const_2**S_BOX_BIT_SIZE) MONOMIALS_ANF_STR = range(NUM_MONOMIALS) for i in range(NUM_MONOMIALS): MONOMIALS[i] = BooleanFunction(MONOMIALS[i]) MONOMIALS_ANF_STR[i] = str(MONOMIALS[i].algebraic_normal_form()) NONLINEAR_INVARIANT_MATRIX = matrix(GF(_sage_const_2), NUM_MONOMIALS) #compute NONLINEAR_INVARIANT_MATRIX for row in range(NUM_MONOMIALS): for col in range(NUM_MONOMIALS): NONLINEAR_INVARIANT_MATRIX[row, col] = _sage_const_0 for mono_idx in range(NUM_MONOMIALS): mono_sbox = BooleanFunction(x0**(_sage_const_0)) # here "1" for var_idx in range(S_BOX_BIT_SIZE): if "x%d" % var_idx in MONOMIALS_ANF_STR[mono_idx]: mono_sbox = mono_sbox * S_BOX_BOOLEANS[var_idx] mono_plus_mono_sbox = MONOMIALS[mono_idx] + mono_sbox mono_plus_mono_sbox_anf_str = str(