Path: blob/master/src/sage/crypto/boolean_function.pyx
8815 views
"""1Boolean functions23Those functions are used for example in LFSR based ciphers like4the filter generator or the combination generator.56This module allows to study properties linked to spectral analysis,7and also algebraic immunity.89EXAMPLES::1011sage: R.<x>=GF(2^8,'a')[]12sage: from sage.crypto.boolean_function import BooleanFunction13sage: B = BooleanFunction( x^254 ) # the Boolean function Tr(x^254)14sage: B15Boolean function with 8 variables16sage: B.nonlinearity()1711218sage: B.algebraic_immunity()1942021AUTHOR:2223- Yann Laigle-Chapuy (2010-02-26): add basic arithmetic24- Yann Laigle-Chapuy (2009-08-28): first implementation2526"""2728from sage.structure.sage_object cimport SageObject2930from sage.rings.integer_ring import ZZ31from sage.rings.integer cimport Integer32from sage.rings.finite_rings.constructor import GF33from sage.rings.polynomial.pbori import BooleanPolynomial34from sage.rings.finite_rings.constructor import is_FiniteField35from sage.rings.finite_rings.finite_field_givaro import FiniteField_givaro36from sage.rings.polynomial.polynomial_element import is_Polynomial3738include "sage/misc/bitset.pxi"39from cpython.string cimport *4041# for details about the implementation of hamming_weight_int,42# walsh_hadamard transform, reed_muller transform, and a lot43# more, see 'Matters computational' available on www.jjj.de.4445cdef inline unsigned int hamming_weight_int(unsigned int x):46# valid for 32bits47x -= (x>>1) & 0x55555555UL # 0-2 in 2 bits48x = ((x>>2) & 0x33333333UL) + (x & 0x33333333UL) # 0-4 in 4 bits49x = ((x>>4) + x) & 0x0f0f0f0fUL # 0-8 in 8 bits50x *= 0x01010101UL51return x>>245253cdef walsh_hadamard(long *f, int ldn):54r"""55The Walsh Hadamard transform is an orthogonal transform equivalent56to a multidimensional discrete Fourier transform of size 2x2x...x2.57It can be defined by the following formula:5859.. math:: W(j) = \sum_{i\in\{0,1\}^n} (-1)^{f(i)\oplus i \cdot j}6061EXAMPLES::6263sage: from sage.crypto.boolean_function import BooleanFunction64sage: B = BooleanFunction([1,0,0,1])65sage: B.walsh_hadamard_transform() # indirect doctest66(0, 0, 0, 4)67"""68cdef long n, ldm, m, mh, t1, t2, r69n = 1 << ldn70for 1 <= ldm <= ldn:71m = (1<<ldm)72mh = m//273for 0 <= r <n by m:74t1 = r75t2 = r+mh76for 0 <= j < mh:77u = f[t1]78v = f[t2]79f[t1] = u + v80f[t2] = u - v81t1 += 182t2 += 18384cdef long yellow_code(unsigned long a):85"""86The yellow-code is just a Reed Muller transform applied to a87word.8889EXAMPLES::9091sage: from sage.crypto.boolean_function import BooleanFunction92sage: R.<x,y,z> = BooleanPolynomialRing(3)93sage: P = x*y94sage: B = BooleanFunction( P )95sage: B.truth_table() # indirect doctest96(False, False, False, True, False, False, False, True)97"""98cdef unsigned long s = (8*sizeof(unsigned long))>>199cdef unsigned long m = (~0UL) >> s100cdef unsigned long r = a101while(s):102r ^= ( (r&m) << s )103s >>= 1104m ^= (m<<s)105return r106107cdef reed_muller(unsigned long *f, int ldn):108r"""109The Reed Muller transform (also known as binary Moebius transform)110is an orthogonal transform. For a function `f` defined by111112.. math:: f(x) = \bigoplus_{I\subset\{1,\ldots,n\}} \left(a_I \prod_{i\in I} x_i\right)113114it allows to compute efficiently the ANF from the truth table and115vice versa, using the formulae:116117.. math:: f(x) = \bigoplus_{support(x)\subset I} a_I118.. math:: a_i = \bigoplus_{I\subset support(x)} f(x)119120121EXAMPLES::122123sage: from sage.crypto.boolean_function import BooleanFunction124sage: R.<x,y,z> = BooleanPolynomialRing(3)125sage: P = x*y126sage: B = BooleanFunction( P )127sage: B.truth_table() # indirect doctest128(False, False, False, True, False, False, False, True)129"""130cdef long n, ldm, m, mh, t1, t2, r131n = 1 << ldn132# intra word transform133for 0 <= r < n:134f[r] = yellow_code(f[r])135# inter word transform136for 1 <= ldm <= ldn:137m = (1<<ldm)138mh = m//2139for 0 <= r <n by m:140t1 = r141t2 = r+mh142for 0 <= j < mh:143f[t2] ^= f[t1]144t1 += 1145t2 += 1146147cdef class BooleanFunction(SageObject):148r"""149This module implements Boolean functions represented as a truth table.150151We can construct a Boolean Function from either:152153- an integer - the result is the zero function with ``x`` variables;154- a list - it is expected to be the truth table of the155result. Therefore it must be of length a power of 2, and its156elements are interpreted as Booleans;157- a string - representing the truth table in hexadecimal;158- a Boolean polynomial - the result is the corresponding Boolean function;159- a polynomial P over an extension of GF(2) - the result is160the Boolean function with truth table ``( Tr(P(x)) for x in161GF(2^k) )``162163EXAMPLES:164165from the number of variables::166167sage: from sage.crypto.boolean_function import BooleanFunction168sage: BooleanFunction(5)169Boolean function with 5 variables170171from a truth table::172173sage: BooleanFunction([1,0,0,1])174Boolean function with 2 variables175176note that elements can be of different types::177178sage: B = BooleanFunction([False, sqrt(2)])179sage: B180Boolean function with 1 variable181sage: [b for b in B]182[False, True]183184from a string::185186sage: BooleanFunction("111e")187Boolean function with 4 variables188189from a :class:`sage.rings.polynomial.pbori.BooleanPolynomial`::190191sage: R.<x,y,z> = BooleanPolynomialRing(3)192sage: P = x*y193sage: BooleanFunction( P )194Boolean function with 3 variables195196from a polynomial over a binary field::197198sage: R.<x> = GF(2^8,'a')[]199sage: B = BooleanFunction( x^7 )200sage: B201Boolean function with 8 variables202203two failure cases::204205sage: BooleanFunction(sqrt(2))206Traceback (most recent call last):207...208TypeError: unable to init the Boolean function209210sage: BooleanFunction([1, 0, 1])211Traceback (most recent call last):212...213ValueError: the length of the truth table must be a power of 2214"""215216cdef bitset_t _truth_table217cdef object _walsh_hadamard_transform218cdef object _nvariables219cdef object _nonlinearity220cdef object _correlation_immunity221cdef object _autocorrelation222cdef object _absolut_indicator223cdef object _sum_of_square_indicator224225def __cinit__(self, x):226r"""227Construct a Boolean Function.228The input ``x`` can be either:229230- an integer - the result is the zero function with ``x`` variables;231- a list - it is expected to be the truth table of the232result. Therefore it must be of length a power of 2, and its233elements are interpreted as Booleans;234- a Boolean polynomial - the result is the corresponding Boolean function;235- a polynomial P over an extension of GF(2) - the result is236the Boolean function with truth table ``( Tr(P(x)) for x in237GF(2^k) )``238239EXAMPLES:240241from the number of variables::242243sage: from sage.crypto.boolean_function import BooleanFunction244sage: BooleanFunction(5)245Boolean function with 5 variables246247from a truth table::248249sage: BooleanFunction([1,0,0,1])250Boolean function with 2 variables251252note that elements can be of different types::253254sage: B = BooleanFunction([False, sqrt(2)])255sage: B256Boolean function with 1 variable257sage: [b for b in B]258[False, True]259260from a :class:`sage.rings.polynomial.pbori.BooleanPolynomial`::261262sage: R.<x,y,z> = BooleanPolynomialRing(3)263sage: P = x*y264sage: BooleanFunction( P )265Boolean function with 3 variables266267from a polynomial over a binary field::268269sage: R.<x> = GF(2^8,'a')[]270sage: B = BooleanFunction( x^7 )271sage: B272Boolean function with 8 variables273274two failure cases::275276sage: BooleanFunction(sqrt(2))277Traceback (most recent call last):278...279TypeError: unable to init the Boolean function280281sage: BooleanFunction([1, 0, 1])282Traceback (most recent call last):283...284ValueError: the length of the truth table must be a power of 2285"""286if PyString_Check(x):287L = ZZ(len(x))288if L.is_power_of(2):289x = ZZ("0x"+x).digits(base=2,padto=4*L)290else:291raise ValueError, "the length of the truth table must be a power of 2"292from types import GeneratorType293if isinstance(x, (list,tuple,GeneratorType)):294# initialisation from a truth table295296# first, check the length297L = ZZ(len(x))298if L.is_power_of(2):299self._nvariables = L.exact_log(2)300else:301raise ValueError, "the length of the truth table must be a power of 2"302303# then, initialize our bitset304bitset_init(self._truth_table, L)305for 0<= i < L:306bitset_set_to(self._truth_table, i, x[i])#int(x[i])&1)307308elif isinstance(x, BooleanPolynomial):309# initialisation from a Boolean polynomial310self._nvariables = ZZ(x.parent().ngens())311bitset_init(self._truth_table, (1<<self._nvariables))312bitset_zero(self._truth_table)313for m in x:314i = sum( [1<<k for k in m.iterindex()] )315bitset_set(self._truth_table, i)316reed_muller(self._truth_table.bits, ZZ(self._truth_table.limbs).exact_log(2) )317318elif isinstance(x, (int,long,Integer) ):319# initialisation to the zero function320self._nvariables = ZZ(x)321bitset_init(self._truth_table,(1<<self._nvariables))322bitset_zero(self._truth_table)323324elif is_Polynomial(x):325K = x.base_ring()326if is_FiniteField(K) and K.characteristic() == 2:327self._nvariables = K.degree()328bitset_init(self._truth_table,(1<<self._nvariables))329bitset_zero(self._truth_table)330if isinstance(K,FiniteField_givaro): #the ordering is not the same in this case331for u in K:332bitset_set_to(self._truth_table, ZZ(u._vector_().list(),2) , (x(u)).trace())333else:334for i,u in enumerate(K):335bitset_set_to(self._truth_table, i , (x(u)).trace())336elif isinstance(x, BooleanFunction):337self._nvariables = x.nvariables()338bitset_init(self._truth_table,(1<<self._nvariables))339bitset_copy(self._truth_table,(<BooleanFunction>x)._truth_table)340else:341raise TypeError, "unable to init the Boolean function"342343def __dealloc__(self):344bitset_free(self._truth_table)345346def _repr_(self):347"""348EXAMPLE::349350sage: from sage.crypto.boolean_function import BooleanFunction351sage: BooleanFunction(4) #indirect doctest352Boolean function with 4 variables353"""354r = "Boolean function with " + self._nvariables.str() + " variable"355if self._nvariables>1:356r += "s"357return r358359def __invert__(self):360"""361Return the complement Boolean function of `self`.362363EXAMPLE::364365sage: from sage.crypto.boolean_function import BooleanFunction366sage: B=BooleanFunction([0, 1, 1, 0, 1, 0, 0, 0])367sage: (~B).truth_table(format='int')368(1, 0, 0, 1, 0, 1, 1, 1)369"""370cdef BooleanFunction res=BooleanFunction(self.nvariables())371bitset_complement(res._truth_table, self._truth_table)372return res373374def __add__(self, BooleanFunction other):375"""376Return the element wise sum of `self`and `other` which must have the same number of variables.377378EXAMPLE::379380sage: from sage.crypto.boolean_function import BooleanFunction381sage: A=BooleanFunction([0, 1, 0, 1, 1, 0, 0, 1])382sage: B=BooleanFunction([0, 1, 1, 0, 1, 0, 0, 0])383sage: (A+B).truth_table(format='int')384(0, 0, 1, 1, 0, 0, 0, 1)385386it also corresponds to the addition of algebraic normal forms::387388sage: S = A.algebraic_normal_form() + B.algebraic_normal_form()389sage: (A+B).algebraic_normal_form() == S390True391392TESTS::393394sage: A+BooleanFunction([0,1])395Traceback (most recent call last):396...397ValueError: the two Boolean functions must have the same number of variables398"""399if (self.nvariables() != other.nvariables() ):400raise ValueError("the two Boolean functions must have the same number of variables")401cdef BooleanFunction res = BooleanFunction(self)402bitset_xor(res._truth_table, res._truth_table, other._truth_table)403return res404405def __mul__(self, BooleanFunction other):406"""407Return the elementwise multiplication of `self`and `other` which must have the same number of variables.408409EXAMPLE::410411sage: from sage.crypto.boolean_function import BooleanFunction412sage: A=BooleanFunction([0, 1, 0, 1, 1, 0, 0, 1])413sage: B=BooleanFunction([0, 1, 1, 0, 1, 0, 0, 0])414sage: (A*B).truth_table(format='int')415(0, 1, 0, 0, 1, 0, 0, 0)416417it also corresponds to the multiplication of algebraic normal forms::418419sage: P = A.algebraic_normal_form() * B.algebraic_normal_form()420sage: (A*B).algebraic_normal_form() == P421True422423TESTS::424425sage: A*BooleanFunction([0,1])426Traceback (most recent call last):427...428ValueError: the two Boolean functions must have the same number of variables429"""430if (self.nvariables() != other.nvariables() ):431raise ValueError("the two Boolean functions must have the same number of variables")432cdef BooleanFunction res = BooleanFunction(self)433bitset_and(res._truth_table, res._truth_table, other._truth_table)434return res435436def __or__(BooleanFunction self, BooleanFunction other):437"""438Return the concatenation of `self` and `other` which must have the same number of variables.439440EXAMPLE::441442sage: from sage.crypto.boolean_function import BooleanFunction443sage: A=BooleanFunction([0, 1, 0, 1])444sage: B=BooleanFunction([0, 1, 1, 0])445sage: (A|B).truth_table(format='int')446(0, 1, 0, 1, 0, 1, 1, 0)447448sage: C = A.truth_table() + B.truth_table()449sage: (A|B).truth_table(format='int') == C450True451452TESTS::453454sage: A|BooleanFunction([0,1])455Traceback (most recent call last):456...457ValueError: the two Boolean functions must have the same number of variables458"""459if (self._nvariables != other.nvariables()):460raise ValueError("the two Boolean functions must have the same number of variables")461462cdef BooleanFunction res=BooleanFunction(self.nvariables()+1)463464nb_limbs = self._truth_table.limbs465if nb_limbs == 1:466L = len(self)467for i in range(L):468res[i ]=self[i]469res[i+L]=other[i]470return res471472memcpy(res._truth_table.bits , self._truth_table.bits, nb_limbs * sizeof(unsigned long))473memcpy(&(res._truth_table.bits[nb_limbs]),other._truth_table.bits, nb_limbs * sizeof(unsigned long))474475return res476477478def algebraic_normal_form(self):479"""480Return the :class:`sage.rings.polynomial.pbori.BooleanPolynomial`481corresponding to the algebraic normal form.482483EXAMPLES::484485sage: from sage.crypto.boolean_function import BooleanFunction486sage: B = BooleanFunction([0,1,1,0,1,0,1,1])487sage: P = B.algebraic_normal_form()488sage: P489x0*x1*x2 + x0 + x1*x2 + x1 + x2490sage: [ P(*ZZ(i).digits(base=2,padto=3)) for i in range(8) ]491[0, 1, 1, 0, 1, 0, 1, 1]492"""493cdef bitset_t anf494bitset_init(anf, (1<<self._nvariables))495bitset_copy(anf, self._truth_table)496reed_muller(anf.bits, ZZ(anf.limbs).exact_log(2))497from sage.rings.polynomial.pbori import BooleanPolynomialRing498R = BooleanPolynomialRing(self._nvariables,"x")499G = R.gens()500P = R(0)501for 0 <= i < anf.limbs:502if anf.bits[i]:503inf = i*sizeof(long)*8504sup = min( (i+1)*sizeof(long)*8 , (1<<self._nvariables) )505for inf <= j < sup:506if bitset_in(anf,j):507m = R(1)508for 0 <= k < self._nvariables:509if (j>>k)&1:510m *= G[k]511P+=m512bitset_free(anf)513return P514515def nvariables(self):516"""517The number of variables of this function.518519EXAMPLE::520521sage: from sage.crypto.boolean_function import BooleanFunction522sage: BooleanFunction(4).nvariables()5234524"""525return self._nvariables526527def truth_table(self,format='bin'):528"""529The truth table of the Boolean function.530531INPUT: a string representing the desired format, can be either532533- 'bin' (default) : we return a tuple of Boolean values534- 'int' : we return a tuple of 0 or 1 values535- 'hex' : we return a string representing the truth_table in hexadecimal536537EXAMPLE::538539sage: from sage.crypto.boolean_function import BooleanFunction540sage: R.<x,y,z> = BooleanPolynomialRing(3)541sage: B = BooleanFunction( x*y*z + z + y + 1 )542sage: B.truth_table()543(True, True, False, False, False, False, True, False)544sage: B.truth_table(format='int')545(1, 1, 0, 0, 0, 0, 1, 0)546sage: B.truth_table(format='hex')547'43'548549sage: BooleanFunction('00ab').truth_table(format='hex')550'00ab'551552sage: B.truth_table(format='oct')553Traceback (most recent call last):554...555ValueError: unknown output format556"""557if format is 'bin':558return tuple(self)559if format is 'int':560return tuple(map(int,self))561if format is 'hex':562S = ""563S = ZZ(self.truth_table(),2).str(16)564S = "0"*((1<<(self._nvariables-2)) - len(S)) + S565for 1 <= i < self._truth_table.limbs:566if sizeof(long)==4:567t = "%04x"%self._truth_table.bits[i]568if sizeof(long)==8:569t = "%08x"%self._truth_table.bits[i]570S = t + S571return S572raise ValueError, "unknown output format"573574def __len__(self):575"""576Return the number of different input values.577578EXAMPLE::579580sage: from sage.crypto.boolean_function import BooleanFunction581sage: len(BooleanFunction(4))58216583"""584return 2**self._nvariables585586def __cmp__(self, other):587"""588Boolean functions are considered to be equal if the number of589input variables is the same, and all the values are equal.590591EXAMPLES::592593sage: from sage.crypto.boolean_function import BooleanFunction594sage: b1 = BooleanFunction([0,1,1,0])595sage: b2 = BooleanFunction([0,1,1,0])596sage: b3 = BooleanFunction([0,1,1,1])597sage: b4 = BooleanFunction([0,1])598sage: b1 == b2599True600sage: b1 == b3601False602sage: b1 == b4603False604"""605cdef BooleanFunction o=other606return bitset_cmp(self._truth_table, o._truth_table)607608def __call__(self, x):609"""610Return the value of the function for the given input.611612INPUT: either613614- a list - then all elements are evaluated as Booleans615- an integer - then we consider its binary representation616617EXAMPLE::618619sage: from sage.crypto.boolean_function import BooleanFunction620sage: B = BooleanFunction([0,1,0,0])621sage: B(1)6221623sage: B([1,0])6241625sage: B(7)626Traceback (most recent call last):627...628IndexError: index out of bound629630"""631if isinstance(x, (int,long,Integer)):632if x > self._truth_table.size:633raise IndexError, "index out of bound"634return bitset_in(self._truth_table,x)635elif isinstance(x, list):636if len(x) != self._nvariables:637raise ValueError, "bad number of inputs"638return self(ZZ(map(bool,x),2))639else:640raise TypeError, "cannot apply Boolean function to provided element"641642def __iter__(self):643"""644Iterate through the value of the function.645646EXAMPLE::647648sage: from sage.crypto.boolean_function import BooleanFunction649sage: B = BooleanFunction([0,1,1,0,1,0,1,0])650sage: [int(b) for b in B]651[0, 1, 1, 0, 1, 0, 1, 0]652653"""654return BooleanFunctionIterator(self)655656def _walsh_hadamard_transform_cached(self):657"""658Return the cached Walsh Hadamard transform. *Unsafe*, no check.659660EXAMPLE::661662sage: from sage.crypto.boolean_function import BooleanFunction663sage: B = BooleanFunction(3)664sage: W = B.walsh_hadamard_transform()665sage: B._walsh_hadamard_transform_cached() is W666True667"""668return self._walsh_hadamard_transform669670def walsh_hadamard_transform(self):671r"""672Compute the Walsh Hadamard transform `W` of the function `f`.673674.. math:: W(j) = \sum_{i\in\{0,1\}^n} (-1)^{f(i)\oplus i \cdot j}675676EXAMPLE::677678sage: from sage.crypto.boolean_function import BooleanFunction679sage: R.<x> = GF(2^3,'a')[]680sage: B = BooleanFunction( x^3 )681sage: B.walsh_hadamard_transform()682(0, 4, 0, -4, 0, -4, 0, -4)683"""684cdef long *temp685686if self._walsh_hadamard_transform is None:687n = self._truth_table.size688temp = <long *>sage_malloc(sizeof(long)*n)689690for 0<= i < n:691temp[i] = (bitset_in(self._truth_table,i)<<1)-1692693walsh_hadamard(temp, self._nvariables)694self._walsh_hadamard_transform = tuple( [temp[i] for i in xrange(n)] )695sage_free(temp)696697return self._walsh_hadamard_transform698699def absolute_walsh_spectrum(self):700"""701Return the absolute Walsh spectrum fo the function.702703EXAMPLES::704705sage: from sage.crypto.boolean_function import BooleanFunction706sage: B = BooleanFunction("7969817CC5893BA6AC326E47619F5AD0")707sage: sorted([_ for _ in B.absolute_walsh_spectrum().iteritems() ])708[(0, 64), (16, 64)]709710sage: B = BooleanFunction("0113077C165E76A8")711sage: B.absolute_walsh_spectrum()712{8: 64}713"""714d = {}715for i in self.walsh_hadamard_transform():716if d.has_key(abs(i)):717d[abs(i)] += 1718else:719d[abs(i)] = 1720return d721722def is_balanced(self):723"""724Return True if the function takes the value True half of the time.725726EXAMPLES::727728sage: from sage.crypto.boolean_function import BooleanFunction729sage: B = BooleanFunction(1)730sage: B.is_balanced()731False732sage: B[0] = True733sage: B.is_balanced()734True735"""736return self.walsh_hadamard_transform()[0] == 0737738def is_symmetric(self):739"""740Return True if the function is symmetric, i.e. invariant under741permutation of its input bits. Another way to see it is that the742output depends only on the Hamming weight of the input.743744EXAMPLES::745746sage: from sage.crypto.boolean_function import BooleanFunction747sage: B = BooleanFunction(5)748sage: B[3] = 1749sage: B.is_symmetric()750False751sage: V_B = [0, 1, 1, 0, 1, 0]752sage: for i in srange(32): B[i] = V_B[i.popcount()]753sage: B.is_symmetric()754True755"""756cdef list T = [ self(2**i-1) for i in range(self._nvariables+1) ]757for i in xrange(2**self._nvariables):758if T[ hamming_weight_int(i) ] != bitset_in(self._truth_table, i):759return False760return True761762def nonlinearity(self):763"""764Return the nonlinearity of the function. This is the distance765to the linear functions, or the number of output ones need to766change to obtain a linear function.767768EXAMPLE::769770sage: from sage.crypto.boolean_function import BooleanFunction771sage: B = BooleanFunction(5)772sage: B[1] = B[3] = 1773sage: B.nonlinearity()7742775sage: B = BooleanFunction("0113077C165E76A8")776sage: B.nonlinearity()77728778"""779if self._nonlinearity is None:780self._nonlinearity = ( (1<<self._nvariables) - max( [abs(w) for w in self.walsh_hadamard_transform()] ) ) >> 1781return self._nonlinearity782783def is_bent(self):784"""785Return True if the function is bent.786787EXAMPLE::788789sage: from sage.crypto.boolean_function import BooleanFunction790sage: B = BooleanFunction("0113077C165E76A8")791sage: B.is_bent()792True793"""794if (self._nvariables & 1):795return False796return self.nonlinearity() == ((1<<self._nvariables)-(1<<(self._nvariables//2)))>>1797798def correlation_immunity(self):799"""800Return the maximum value `m` such that the function is801correlation immune of order `m`.802803A Boolean function is said to be correlation immune of order804`m` , if the output of the function is statistically805independent of the combination of any m of its inputs.806807EXAMPLES::808809sage: from sage.crypto.boolean_function import BooleanFunction810sage: B = BooleanFunction("7969817CC5893BA6AC326E47619F5AD0")811sage: B.correlation_immunity()8122813"""814cdef int c815if self._correlation_immunity is None:816c = self._nvariables817W = self.walsh_hadamard_transform()818for 0 < i < len(W):819if (W[i] != 0):820c = min( c , hamming_weight_int(i) )821self._correlation_immunity = ZZ(c-1)822return self._correlation_immunity823824def resiliency_order(self):825"""826Return the maximum value `m` such that the function is827resilient of order `m`.828829A Boolean function is said to be resilient of order `m` if it830is balanced and correlation immune of order `m`.831832If the function is not balanced, we return -1.833834EXAMPLES::835836sage: from sage.crypto.boolean_function import BooleanFunction837sage: B = BooleanFunction("077CE5A2F8831A5DF8831A5D077CE5A26996699669699696669999665AA5A55A")838sage: B.resiliency_order()8393840"""841if not self.is_balanced():842return -1843return self.correlation_immunity()844845def autocorrelation(self):846r"""847Return the autocorrelation fo the function, defined by848849.. math:: \Delta_f(j) = \sum_{i\in\{0,1\}^n} (-1)^{f(i)\oplus f(i\oplus j)}.850851EXAMPLES::852853sage: from sage.crypto.boolean_function import BooleanFunction854sage: B = BooleanFunction("03")855sage: B.autocorrelation()856(8, 8, 0, 0, 0, 0, 0, 0)857"""858cdef long *temp859860if self._autocorrelation is None:861n = self._truth_table.size862temp = <long *>sage_malloc(sizeof(long)*n)863W = self.walsh_hadamard_transform()864865for 0<= i < n:866temp[i] = W[i]*W[i]867868walsh_hadamard(temp, self._nvariables)869self._autocorrelation = tuple( [temp[i]>>self._nvariables for i in xrange(n)] )870sage_free(temp)871872return self._autocorrelation873874def absolute_autocorrelation(self):875"""876Return the absolute autocorrelation of the function.877878EXAMPLES::879880sage: from sage.crypto.boolean_function import BooleanFunction881sage: B = BooleanFunction("7969817CC5893BA6AC326E47619F5AD0")882sage: sorted([ _ for _ in B.absolute_autocorrelation().iteritems() ])883[(0, 33), (8, 58), (16, 28), (24, 6), (32, 2), (128, 1)]884"""885d = {}886for i in self.autocorrelation():887if d.has_key(abs(i)):888d[abs(i)] += 1889else:890d[abs(i)] = 1891return d892893def absolut_indicator(self):894"""895Return the absolut indicator of the function. Ths is the maximal absolut896value of the autocorrelation.897898EXAMPLES::899900sage: from sage.crypto.boolean_function import BooleanFunction901sage: B = BooleanFunction("7969817CC5893BA6AC326E47619F5AD0")902sage: B.absolut_indicator()90332904"""905if self._absolut_indicator is None:906D = self.autocorrelation()907self._absolut_indicator = max([ abs(a) for a in D[1:] ])908return self._absolut_indicator909910def sum_of_square_indicator(self):911"""912Return the sum of square indicator of the function.913914EXAMPLES::915916sage: from sage.crypto.boolean_function import BooleanFunction917sage: B = BooleanFunction("7969817CC5893BA6AC326E47619F5AD0")918sage: B.sum_of_square_indicator()91932768920"""921if self._sum_of_square_indicator is None:922D = self.autocorrelation()923self._sum_of_square_indicator = sum([ a**2 for a in D ])924return self._sum_of_square_indicator925926def annihilator(self,d, dim = False):927r"""928Return (if it exists) an annihilator of the boolean function of929degree at most `d`, that is a Boolean polynomial `g` such that930931.. math::932933f(x)g(x) = 0 \forall x.934935INPUT:936937- ``d`` -- an integer;938- ``dim`` -- a Boolean (default: False), if True, return also939the dimension of the annihilator vector space.940941EXAMPLES::942943sage: from sage.crypto.boolean_function import BooleanFunction944sage: f = BooleanFunction("7969817CC5893BA6AC326E47619F5AD0")945sage: f.annihilator(1) is None946True947sage: g = BooleanFunction( f.annihilator(3) )948sage: set([ fi*g(i) for i,fi in enumerate(f) ])949set([0])950"""951# NOTE: this is a toy implementation952from sage.rings.polynomial.pbori import BooleanPolynomialRing953R = BooleanPolynomialRing(self._nvariables,'x')954G = R.gens()955r = [R(1)]956957from sage.modules.all import vector958s = vector(self.truth_table()).support()959960from sage.combinat.combination import Combinations961from sage.misc.misc import prod962963from sage.matrix.constructor import Matrix964from sage.rings.arith import binomial965M = Matrix(GF(2),sum([binomial(self._nvariables,i) for i in xrange(d+1)]),len(s))966967for i in xrange(1,d+1):968C = Combinations(self._nvariables,i)969for c in C:970r.append(prod([G[i] for i in c]))971972cdef BooleanFunction t973974for i,m in enumerate(r):975t = BooleanFunction(m)976for j,v in enumerate(s):977M[i,j] = bitset_in(t._truth_table,v)978979kg = M.kernel().gens()980981if len(kg)>0:982res = sum([kg[0][i]*ri for i,ri in enumerate(r)])983else:984res = None985986if dim:987return res,len(kg)988else:989return res990991def algebraic_immunity(self, annihilator = False):992"""993Returns the algebraic immunity of the Boolean function. This is the smallest994integer `i` such that there exists a non trivial annihilator for `self` or `~self`.995996INPUT:997998- annihilator - a Boolean (default: False), if True, returns also an annihilator of minimal degree.9991000EXAMPLES::10011002sage: from sage.crypto.boolean_function import BooleanFunction1003sage: R.<x0,x1,x2,x3,x4,x5> = BooleanPolynomialRing(6)1004sage: B = BooleanFunction(x0*x1 + x1*x2 + x2*x3 + x3*x4 + x4*x5)1005sage: B.algebraic_immunity(annihilator=True)1006(2, x0*x1 + x1*x2 + x2*x3 + x3*x4 + x4*x5 + 1)1007sage: B[0] +=11008sage: B.algebraic_immunity()1009210101011sage: R.<x> = GF(2^8,'a')[]1012sage: B = BooleanFunction(x^31)1013sage: B.algebraic_immunity()101441015"""1016f = self1017g = ~self1018for i in xrange(self._nvariables):1019for fun in [f,g]:1020A = fun.annihilator(i)1021if A is not None:1022if annihilator:1023return i,A1024else:1025return i1026raise ValueError, "you just found a bug!"10271028def __setitem__(self, i, y):1029"""1030Set a value of the function.10311032EXAMPLE::10331034sage: from sage.crypto.boolean_function import BooleanFunction1035sage: B=BooleanFunction([0,0,1,1])1036sage: B[0]=11037sage: B[2]=(3**17 == 9)1038sage: [b for b in B]1039[True, False, False, True]10401041We take care to clear cached values::10421043sage: W = B.walsh_hadamard_transform()1044sage: B[2] = 11045sage: B._walsh_hadamard_transform_cached() is None1046True1047"""1048self._clear_cache()1049bitset_set_to(self._truth_table, int(i), int(y)&1)10501051def __getitem__(self, i):1052"""1053Return the value of the function for the given input.10541055EXAMPLE::10561057sage: from sage.crypto.boolean_function import BooleanFunction1058sage: B=BooleanFunction([0,1,1,1])1059sage: [ int(B[i]) for i in range(len(B)) ]1060[0, 1, 1, 1]1061"""1062return self.__call__(i)10631064def _clear_cache(self):1065"""1066Clear cached values.10671068EXAMPLE::10691070sage: from sage.crypto.boolean_function import BooleanFunction1071sage: B = BooleanFunction([0,1,1,0])1072sage: W = B.walsh_hadamard_transform()1073sage: B._walsh_hadamard_transform_cached() is None1074False1075sage: B._clear_cache()1076sage: B._walsh_hadamard_transform_cached() is None1077True1078"""1079self._walsh_hadamard_transform = None1080self._nonlinearity = None1081self._correlation_immunity = None1082self._autocorrelation = None1083self._absolut_indicator = None1084self._sum_of_square_indicator = None10851086def __reduce__(self):1087"""1088EXAMPLE::10891090sage: from sage.crypto.boolean_function import BooleanFunction1091sage: B = BooleanFunction([0,1,1,0])1092sage: loads(dumps(B)) == B1093True1094"""1095return unpickle_BooleanFunction, (self.truth_table(format='hex'),)10961097def unpickle_BooleanFunction(bool_list):1098"""1099Specific function to unpickle Boolean functions.11001101EXAMPLE::11021103sage: from sage.crypto.boolean_function import BooleanFunction1104sage: B = BooleanFunction([0,1,1,0])1105sage: loads(dumps(B)) == B # indirect doctest1106True1107"""1108return BooleanFunction(bool_list)11091110cdef class BooleanFunctionIterator:1111cdef long index, last1112cdef BooleanFunction f11131114def __init__(self, f):1115"""1116Iterator through the values of a Boolean function.11171118EXAMPLE::11191120sage: from sage.crypto.boolean_function import BooleanFunction1121sage: B = BooleanFunction(3)1122sage: type(B.__iter__())1123<type 'sage.crypto.boolean_function.BooleanFunctionIterator'>1124"""1125self.f = f1126self.index = -11127self.last = self.f._truth_table.size-111281129def __iter__(self):1130"""1131Iterator through the values of a Boolean function.11321133EXAMPLE::11341135sage: from sage.crypto.boolean_function import BooleanFunction1136sage: B = BooleanFunction(1)1137sage: [b for b in B] # indirect doctest1138[False, False]1139"""1140return self11411142def __next__(self):1143"""1144Next value.11451146EXAMPLE::11471148sage: from sage.crypto.boolean_function import BooleanFunction1149sage: B = BooleanFunction(1)1150sage: I = B.__iter__()1151sage: I.next()1152False1153"""1154if self.index == self.last:1155raise StopIteration1156self.index += 11157return bitset_in(self.f._truth_table, self.index)11581159##########################################1160# Below we provide some constructions of #1161# cryptographic Boolean function. #1162##########################################11631164def random_boolean_function(n):1165"""1166Returns a random Boolean function with `n` variables.11671168EXAMPLE::11691170sage: from sage.crypto.boolean_function import random_boolean_function1171sage: B = random_boolean_function(9)1172sage: B.nvariables()117391174sage: B.nonlinearity()1175217 # 32-bit1176222 # 64-bit1177"""1178from sage.misc.randstate import current_randstate1179r = current_randstate().python_random()1180cdef BooleanFunction B = BooleanFunction(n)1181cdef bitset_t T1182T[0] = B._truth_table[0]1183for 0 <= i < T.limbs:1184T.bits[i] = r.randrange(0,Integer(1)<<(sizeof(unsigned long)*8))1185return B118611871188