Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/crypto/boolean_function.pyx
4036 views
1
"""
2
Boolean functions
3
4
Those functions are used for example in LFSR based ciphers like
5
the filter generator or the combination generator.
6
7
This module allows to study properties linked to spectral analysis,
8
and also algebraic immunity.
9
10
EXAMPLES::
11
12
sage: R.<x>=GF(2^8,'a')[]
13
sage: from sage.crypto.boolean_function import BooleanFunction
14
sage: B = BooleanFunction( x^254 ) # the Boolean function Tr(x^254)
15
sage: B
16
Boolean function with 8 variables
17
sage: B.nonlinearity()
18
112
19
sage: B.algebraic_immunity()
20
4
21
22
AUTHOR:
23
24
- Yann Laigle-Chapuy (2010-02-26): add basic arithmetic
25
- Yann Laigle-Chapuy (2009-08-28): first implementation
26
27
"""
28
29
from sage.structure.sage_object cimport SageObject
30
31
from sage.rings.integer_ring import ZZ
32
from sage.rings.integer cimport Integer
33
from sage.rings.finite_rings.constructor import GF
34
from sage.rings.polynomial.pbori import BooleanPolynomial
35
from sage.rings.finite_rings.all import is_FiniteField
36
from sage.rings.finite_rings.finite_field_givaro import FiniteField_givaro
37
from sage.rings.polynomial.polynomial_element import is_Polynomial
38
39
include "sage/misc/bitset_pxd.pxi"
40
include "sage/misc/bitset.pxi"
41
include "../ext/python_string.pxi"
42
43
# for details about the implementation of hamming_weight_int,
44
# walsh_hadamard transform, reed_muller transform, and a lot
45
# more, see 'Matters computational' available on www.jjj.de.
46
47
cdef inline unsigned int hamming_weight_int(unsigned int x):
48
# valid for 32bits
49
x -= (x>>1) & 0x55555555UL # 0-2 in 2 bits
50
x = ((x>>2) & 0x33333333UL) + (x & 0x33333333UL) # 0-4 in 4 bits
51
x = ((x>>4) + x) & 0x0f0f0f0fUL # 0-8 in 8 bits
52
x *= 0x01010101UL
53
return x>>24
54
55
cdef walsh_hadamard(long *f, int ldn):
56
r"""
57
The Walsh Hadamard transform is an orthogonal transform equivalent
58
to a multidimensional discrete Fourier transform of size 2x2x...x2.
59
It can be defined by the following formula:
60
61
.. math:: W(j) = \sum_{i\in\{0,1\}^n} (-1)^{f(i)\oplus i \cdot j}
62
63
EXAMPLES::
64
65
sage: from sage.crypto.boolean_function import BooleanFunction
66
sage: B = BooleanFunction([1,0,0,1])
67
sage: B.walsh_hadamard_transform() # indirect doctest
68
(0, 0, 0, 4)
69
"""
70
cdef long n, ldm, m, mh, t1, t2, r
71
n = 1 << ldn
72
for 1 <= ldm <= ldn:
73
m = (1<<ldm)
74
mh = m//2
75
for 0 <= r <n by m:
76
t1 = r
77
t2 = r+mh
78
for 0 <= j < mh:
79
u = f[t1]
80
v = f[t2]
81
f[t1] = u + v
82
f[t2] = u - v
83
t1 += 1
84
t2 += 1
85
86
cdef long yellow_code(unsigned long a):
87
"""
88
The yellow-code is just a Reed Muller transform applied to a
89
word.
90
91
EXAMPLES::
92
93
sage: from sage.crypto.boolean_function import BooleanFunction
94
sage: R.<x,y,z> = BooleanPolynomialRing(3)
95
sage: P = x*y
96
sage: B = BooleanFunction( P )
97
sage: B.truth_table() # indirect doctest
98
(False, False, False, True, False, False, False, True)
99
"""
100
cdef unsigned long s = (8*sizeof(unsigned long))>>1
101
cdef unsigned long m = (~0UL) >> s
102
cdef unsigned long r = a
103
while(s):
104
r ^= ( (r&m) << s )
105
s >>= 1
106
m ^= (m<<s)
107
return r
108
109
cdef reed_muller(unsigned long *f, int ldn):
110
r"""
111
The Reed Muller transform (also known as binary Moebius transform)
112
is an orthogonal transform. For a function `f` defined by
113
114
.. math:: f(x) = \bigoplus_{I\subset\{1,\ldots,n\}} \left(a_I \prod_{i\in I} x_i\right)
115
116
it allows to compute efficiently the ANF from the truth table and
117
vice versa, using the formulae:
118
119
.. math:: f(x) = \bigoplus_{support(x)\subset I} a_I
120
.. math:: a_i = \bigoplus_{I\subset support(x)} f(x)
121
122
123
EXAMPLES::
124
125
sage: from sage.crypto.boolean_function import BooleanFunction
126
sage: R.<x,y,z> = BooleanPolynomialRing(3)
127
sage: P = x*y
128
sage: B = BooleanFunction( P )
129
sage: B.truth_table() # indirect doctest
130
(False, False, False, True, False, False, False, True)
131
"""
132
cdef long n, ldm, m, mh, t1, t2, r
133
n = 1 << ldn
134
# intra word transform
135
for 0 <= r < n:
136
f[r] = yellow_code(f[r])
137
# inter word transform
138
for 1 <= ldm <= ldn:
139
m = (1<<ldm)
140
mh = m//2
141
for 0 <= r <n by m:
142
t1 = r
143
t2 = r+mh
144
for 0 <= j < mh:
145
f[t2] ^= f[t1]
146
t1 += 1
147
t2 += 1
148
149
cdef class BooleanFunction(SageObject):
150
r"""
151
This module implements Boolean functions represented as a truth table.
152
153
We can construct a Boolean Function from either:
154
155
- an integer - the result is the zero function with ``x`` variables;
156
- a list - it is expected to be the truth table of the
157
result. Therefore it must be of length a power of 2, and its
158
elements are interpreted as Booleans;
159
- a string - representing the truth table in hexadecimal;
160
- a Boolean polynomial - the result is the corresponding Boolean function;
161
- a polynomial P over an extension of GF(2) - the result is
162
the Boolean function with truth table ``( Tr(P(x)) for x in
163
GF(2^k) )``
164
165
EXAMPLES:
166
167
from the number of variables::
168
169
sage: from sage.crypto.boolean_function import BooleanFunction
170
sage: BooleanFunction(5)
171
Boolean function with 5 variables
172
173
from a truth table::
174
175
sage: BooleanFunction([1,0,0,1])
176
Boolean function with 2 variables
177
178
note that elements can be of different types::
179
180
sage: B = BooleanFunction([False, sqrt(2)])
181
sage: B
182
Boolean function with 1 variable
183
sage: [b for b in B]
184
[False, True]
185
186
from a string::
187
188
sage: BooleanFunction("111e")
189
Boolean function with 4 variables
190
191
from a :class:`sage.rings.polynomial.pbori.BooleanPolynomial`::
192
193
sage: R.<x,y,z> = BooleanPolynomialRing(3)
194
sage: P = x*y
195
sage: BooleanFunction( P )
196
Boolean function with 3 variables
197
198
from a polynomial over a binary field::
199
200
sage: R.<x> = GF(2^8,'a')[]
201
sage: B = BooleanFunction( x^7 )
202
sage: B
203
Boolean function with 8 variables
204
205
two failure cases::
206
207
sage: BooleanFunction(sqrt(2))
208
Traceback (most recent call last):
209
...
210
TypeError: unable to init the Boolean function
211
212
sage: BooleanFunction([1, 0, 1])
213
Traceback (most recent call last):
214
...
215
ValueError: the length of the truth table must be a power of 2
216
"""
217
218
cdef bitset_t _truth_table
219
cdef object _walsh_hadamard_transform
220
cdef object _nvariables
221
cdef object _nonlinearity
222
cdef object _correlation_immunity
223
cdef object _autocorrelation
224
cdef object _absolut_indicator
225
cdef object _sum_of_square_indicator
226
227
def __cinit__(self, x):
228
r"""
229
Construct a Boolean Function.
230
The input ``x`` can be either:
231
232
- an integer - the result is the zero function with ``x`` variables;
233
- a list - it is expected to be the truth table of the
234
result. Therefore it must be of length a power of 2, and its
235
elements are interpreted as Booleans;
236
- a Boolean polynomial - the result is the corresponding Boolean function;
237
- a polynomial P over an extension of GF(2) - the result is
238
the Boolean function with truth table ``( Tr(P(x)) for x in
239
GF(2^k) )``
240
241
EXAMPLES:
242
243
from the number of variables::
244
245
sage: from sage.crypto.boolean_function import BooleanFunction
246
sage: BooleanFunction(5)
247
Boolean function with 5 variables
248
249
from a truth table::
250
251
sage: BooleanFunction([1,0,0,1])
252
Boolean function with 2 variables
253
254
note that elements can be of different types::
255
256
sage: B = BooleanFunction([False, sqrt(2)])
257
sage: B
258
Boolean function with 1 variable
259
sage: [b for b in B]
260
[False, True]
261
262
from a :class:`sage.rings.polynomial.pbori.BooleanPolynomial`::
263
264
sage: R.<x,y,z> = BooleanPolynomialRing(3)
265
sage: P = x*y
266
sage: BooleanFunction( P )
267
Boolean function with 3 variables
268
269
from a polynomial over a binary field::
270
271
sage: R.<x> = GF(2^8,'a')[]
272
sage: B = BooleanFunction( x^7 )
273
sage: B
274
Boolean function with 8 variables
275
276
two failure cases::
277
278
sage: BooleanFunction(sqrt(2))
279
Traceback (most recent call last):
280
...
281
TypeError: unable to init the Boolean function
282
283
sage: BooleanFunction([1, 0, 1])
284
Traceback (most recent call last):
285
...
286
ValueError: the length of the truth table must be a power of 2
287
"""
288
if PyString_Check(x):
289
L = ZZ(len(x))
290
if L.is_power_of(2):
291
x = ZZ("0x"+x).digits(base=2,padto=4*L)
292
else:
293
raise ValueError, "the length of the truth table must be a power of 2"
294
from types import GeneratorType
295
if isinstance(x, (list,tuple,GeneratorType)):
296
# initialisation from a truth table
297
298
# first, check the length
299
L = ZZ(len(x))
300
if L.is_power_of(2):
301
self._nvariables = L.exact_log(2)
302
else:
303
raise ValueError, "the length of the truth table must be a power of 2"
304
305
# then, initialize our bitset
306
bitset_init(self._truth_table, L)
307
for 0<= i < L:
308
bitset_set_to(self._truth_table, i, x[i])#int(x[i])&1)
309
310
elif isinstance(x, BooleanPolynomial):
311
# initialisation from a Boolean polynomial
312
self._nvariables = ZZ(x.parent().ngens())
313
bitset_init(self._truth_table, (1<<self._nvariables))
314
bitset_zero(self._truth_table)
315
for m in x:
316
i = sum( [1<<k for k in m.iterindex()] )
317
bitset_set(self._truth_table, i)
318
reed_muller(self._truth_table.bits, ZZ(self._truth_table.limbs).exact_log(2) )
319
320
elif isinstance(x, (int,long,Integer) ):
321
# initialisation to the zero function
322
self._nvariables = ZZ(x)
323
bitset_init(self._truth_table,(1<<self._nvariables))
324
bitset_zero(self._truth_table)
325
326
elif is_Polynomial(x):
327
K = x.base_ring()
328
if is_FiniteField(K) and K.characteristic() == 2:
329
self._nvariables = K.degree()
330
bitset_init(self._truth_table,(1<<self._nvariables))
331
bitset_zero(self._truth_table)
332
if isinstance(K,FiniteField_givaro): #the ordering is not the same in this case
333
for u in K:
334
bitset_set_to(self._truth_table, ZZ(u._vector_().list(),2) , (x(u)).trace())
335
else:
336
for i,u in enumerate(K):
337
bitset_set_to(self._truth_table, i , (x(u)).trace())
338
elif isinstance(x, BooleanFunction):
339
self._nvariables = x.nvariables()
340
bitset_init(self._truth_table,(1<<self._nvariables))
341
bitset_copy(self._truth_table,(<BooleanFunction>x)._truth_table)
342
else:
343
raise TypeError, "unable to init the Boolean function"
344
345
def __dealloc__(self):
346
bitset_free(self._truth_table)
347
348
def _repr_(self):
349
"""
350
EXAMPLE::
351
352
sage: from sage.crypto.boolean_function import BooleanFunction
353
sage: BooleanFunction(4) #indirect doctest
354
Boolean function with 4 variables
355
"""
356
r = "Boolean function with " + self._nvariables.str() + " variable"
357
if self._nvariables>1:
358
r += "s"
359
return r
360
361
def __invert__(self):
362
"""
363
Return the complement Boolean function of `self`.
364
365
EXAMPLE::
366
367
sage: from sage.crypto.boolean_function import BooleanFunction
368
sage: B=BooleanFunction([0, 1, 1, 0, 1, 0, 0, 0])
369
sage: (~B).truth_table(format='int')
370
(1, 0, 0, 1, 0, 1, 1, 1)
371
"""
372
cdef BooleanFunction res=BooleanFunction(self.nvariables())
373
bitset_complement(res._truth_table, self._truth_table)
374
return res
375
376
def __add__(self, BooleanFunction other):
377
"""
378
Return the element wise sum of `self`and `other` which must have the same number of variables.
379
380
EXAMPLE::
381
382
sage: from sage.crypto.boolean_function import BooleanFunction
383
sage: A=BooleanFunction([0, 1, 0, 1, 1, 0, 0, 1])
384
sage: B=BooleanFunction([0, 1, 1, 0, 1, 0, 0, 0])
385
sage: (A+B).truth_table(format='int')
386
(0, 0, 1, 1, 0, 0, 0, 1)
387
388
it also corresponds to the addition of algebraic normal forms::
389
390
sage: S = A.algebraic_normal_form() + B.algebraic_normal_form()
391
sage: (A+B).algebraic_normal_form() == S
392
True
393
394
TESTS::
395
396
sage: A+BooleanFunction([0,1])
397
Traceback (most recent call last):
398
...
399
ValueError: the two Boolean functions must have the same number of variables
400
"""
401
if (self.nvariables() != other.nvariables() ):
402
raise ValueError("the two Boolean functions must have the same number of variables")
403
cdef BooleanFunction res = BooleanFunction(self)
404
bitset_xor(res._truth_table, res._truth_table, other._truth_table)
405
return res
406
407
def __mul__(self, BooleanFunction other):
408
"""
409
Return the elementwise multiplication of `self`and `other` which must have the same number of variables.
410
411
EXAMPLE::
412
413
sage: from sage.crypto.boolean_function import BooleanFunction
414
sage: A=BooleanFunction([0, 1, 0, 1, 1, 0, 0, 1])
415
sage: B=BooleanFunction([0, 1, 1, 0, 1, 0, 0, 0])
416
sage: (A*B).truth_table(format='int')
417
(0, 1, 0, 0, 1, 0, 0, 0)
418
419
it also corresponds to the multiplication of algebraic normal forms::
420
421
sage: P = A.algebraic_normal_form() * B.algebraic_normal_form()
422
sage: (A*B).algebraic_normal_form() == P
423
True
424
425
TESTS::
426
427
sage: A*BooleanFunction([0,1])
428
Traceback (most recent call last):
429
...
430
ValueError: the two Boolean functions must have the same number of variables
431
"""
432
if (self.nvariables() != other.nvariables() ):
433
raise ValueError("the two Boolean functions must have the same number of variables")
434
cdef BooleanFunction res = BooleanFunction(self)
435
bitset_and(res._truth_table, res._truth_table, other._truth_table)
436
return res
437
438
def __or__(BooleanFunction self, BooleanFunction other):
439
"""
440
Return the concatenation of `self` and `other` which must have the same number of variables.
441
442
EXAMPLE::
443
444
sage: from sage.crypto.boolean_function import BooleanFunction
445
sage: A=BooleanFunction([0, 1, 0, 1])
446
sage: B=BooleanFunction([0, 1, 1, 0])
447
sage: (A|B).truth_table(format='int')
448
(0, 1, 0, 1, 0, 1, 1, 0)
449
450
sage: C = A.truth_table() + B.truth_table()
451
sage: (A|B).truth_table(format='int') == C
452
True
453
454
TESTS::
455
456
sage: A|BooleanFunction([0,1])
457
Traceback (most recent call last):
458
...
459
ValueError: the two Boolean functions must have the same number of variables
460
"""
461
if (self._nvariables != other.nvariables()):
462
raise ValueError("the two Boolean functions must have the same number of variables")
463
464
cdef BooleanFunction res=BooleanFunction(self.nvariables()+1)
465
466
nb_limbs = self._truth_table.limbs
467
if nb_limbs == 1:
468
L = len(self)
469
for i in range(L):
470
res[i ]=self[i]
471
res[i+L]=other[i]
472
return res
473
474
memcpy(res._truth_table.bits , self._truth_table.bits, nb_limbs * sizeof(unsigned long))
475
memcpy(&(res._truth_table.bits[nb_limbs]),other._truth_table.bits, nb_limbs * sizeof(unsigned long))
476
477
return res
478
479
480
def algebraic_normal_form(self):
481
"""
482
Return the :class:`sage.rings.polynomial.pbori.BooleanPolynomial`
483
corresponding to the algebraic normal form.
484
485
EXAMPLES::
486
487
sage: from sage.crypto.boolean_function import BooleanFunction
488
sage: B = BooleanFunction([0,1,1,0,1,0,1,1])
489
sage: P = B.algebraic_normal_form()
490
sage: P
491
x0*x1*x2 + x0 + x1*x2 + x1 + x2
492
sage: [ P(*ZZ(i).digits(base=2,padto=3)) for i in range(8) ]
493
[0, 1, 1, 0, 1, 0, 1, 1]
494
"""
495
cdef bitset_t anf
496
bitset_init(anf, (1<<self._nvariables))
497
bitset_copy(anf, self._truth_table)
498
reed_muller(anf.bits, ZZ(anf.limbs).exact_log(2))
499
from sage.rings.polynomial.pbori import BooleanPolynomialRing
500
R = BooleanPolynomialRing(self._nvariables,"x")
501
G = R.gens()
502
P = R(0)
503
for 0 <= i < anf.limbs:
504
if anf.bits[i]:
505
inf = i*sizeof(long)*8
506
sup = min( (i+1)*sizeof(long)*8 , (1<<self._nvariables) )
507
for inf <= j < sup:
508
if bitset_in(anf,j):
509
m = R(1)
510
for 0 <= k < self._nvariables:
511
if (j>>k)&1:
512
m *= G[k]
513
P+=m
514
bitset_free(anf)
515
return P
516
517
def nvariables(self):
518
"""
519
The number of variables of this function.
520
521
EXAMPLE::
522
523
sage: from sage.crypto.boolean_function import BooleanFunction
524
sage: BooleanFunction(4).nvariables()
525
4
526
"""
527
return self._nvariables
528
529
def truth_table(self,format='bin'):
530
"""
531
The truth table of the Boolean function.
532
533
INPUT: a string representing the desired format, can be either
534
535
- 'bin' (default) : we return a tuple of Boolean values
536
- 'int' : we return a tuple of 0 or 1 values
537
- 'hex' : we return a string representing the truth_table in hexadecimal
538
539
EXAMPLE::
540
541
sage: from sage.crypto.boolean_function import BooleanFunction
542
sage: R.<x,y,z> = BooleanPolynomialRing(3)
543
sage: B = BooleanFunction( x*y*z + z + y + 1 )
544
sage: B.truth_table()
545
(True, True, False, False, False, False, True, False)
546
sage: B.truth_table(format='int')
547
(1, 1, 0, 0, 0, 0, 1, 0)
548
sage: B.truth_table(format='hex')
549
'43'
550
551
sage: BooleanFunction('00ab').truth_table(format='hex')
552
'00ab'
553
554
sage: B.truth_table(format='oct')
555
Traceback (most recent call last):
556
...
557
ValueError: unknown output format
558
"""
559
if format is 'bin':
560
return tuple(self)
561
if format is 'int':
562
return tuple(map(int,self))
563
if format is 'hex':
564
S = ""
565
S = ZZ(self.truth_table(),2).str(16)
566
S = "0"*((1<<(self._nvariables-2)) - len(S)) + S
567
for 1 <= i < self._truth_table.limbs:
568
if sizeof(long)==4:
569
t = "%04x"%self._truth_table.bits[i]
570
if sizeof(long)==8:
571
t = "%08x"%self._truth_table.bits[i]
572
S = t + S
573
return S
574
raise ValueError, "unknown output format"
575
576
def __len__(self):
577
"""
578
Return the number of different input values.
579
580
EXAMPLE::
581
582
sage: from sage.crypto.boolean_function import BooleanFunction
583
sage: len(BooleanFunction(4))
584
16
585
"""
586
return 2**self._nvariables
587
588
def __cmp__(self, other):
589
"""
590
Boolean functions are considered to be equal if the number of
591
input variables is the same, and all the values are equal.
592
593
EXAMPLES::
594
595
sage: from sage.crypto.boolean_function import BooleanFunction
596
sage: b1 = BooleanFunction([0,1,1,0])
597
sage: b2 = BooleanFunction([0,1,1,0])
598
sage: b3 = BooleanFunction([0,1,1,1])
599
sage: b4 = BooleanFunction([0,1])
600
sage: b1 == b2
601
True
602
sage: b1 == b3
603
False
604
sage: b1 == b4
605
False
606
"""
607
cdef BooleanFunction o=other
608
return bitset_cmp(self._truth_table, o._truth_table)
609
610
def __call__(self, x):
611
"""
612
Return the value of the function for the given input.
613
614
INPUT: either
615
616
- a list - then all elements are evaluated as Booleans
617
- an integer - then we consider its binary representation
618
619
EXAMPLE::
620
621
sage: from sage.crypto.boolean_function import BooleanFunction
622
sage: B = BooleanFunction([0,1,0,0])
623
sage: B(1)
624
1
625
sage: B([1,0])
626
1
627
sage: B(7)
628
Traceback (most recent call last):
629
...
630
IndexError: index out of bound
631
632
"""
633
if isinstance(x, (int,long,Integer)):
634
if x > self._truth_table.size:
635
raise IndexError, "index out of bound"
636
return bitset_in(self._truth_table,x)
637
elif isinstance(x, list):
638
if len(x) != self._nvariables:
639
raise ValueError, "bad number of inputs"
640
return self(ZZ(map(bool,x),2))
641
else:
642
raise TypeError, "cannot apply Boolean function to provided element"
643
644
def __iter__(self):
645
"""
646
Iterate through the value of the function.
647
648
EXAMPLE::
649
650
sage: from sage.crypto.boolean_function import BooleanFunction
651
sage: B = BooleanFunction([0,1,1,0,1,0,1,0])
652
sage: [int(b) for b in B]
653
[0, 1, 1, 0, 1, 0, 1, 0]
654
655
"""
656
return BooleanFunctionIterator(self)
657
658
def _walsh_hadamard_transform_cached(self):
659
"""
660
Return the cached Walsh Hadamard transform. *Unsafe*, no check.
661
662
EXAMPLE::
663
664
sage: from sage.crypto.boolean_function import BooleanFunction
665
sage: B = BooleanFunction(3)
666
sage: W = B.walsh_hadamard_transform()
667
sage: B._walsh_hadamard_transform_cached() is W
668
True
669
"""
670
return self._walsh_hadamard_transform
671
672
def walsh_hadamard_transform(self):
673
r"""
674
Compute the Walsh Hadamard transform `W` of the function `f`.
675
676
.. math:: W(j) = \sum_{i\in\{0,1\}^n} (-1)^{f(i)\oplus i \cdot j}
677
678
EXAMPLE::
679
680
sage: from sage.crypto.boolean_function import BooleanFunction
681
sage: R.<x> = GF(2^3,'a')[]
682
sage: B = BooleanFunction( x^3 )
683
sage: B.walsh_hadamard_transform()
684
(0, 4, 0, -4, 0, -4, 0, -4)
685
"""
686
cdef long *temp
687
688
if self._walsh_hadamard_transform is None:
689
n = self._truth_table.size
690
temp = <long *>sage_malloc(sizeof(long)*n)
691
692
for 0<= i < n:
693
temp[i] = (bitset_in(self._truth_table,i)<<1)-1
694
695
walsh_hadamard(temp, self._nvariables)
696
self._walsh_hadamard_transform = tuple( [temp[i] for i in xrange(n)] )
697
sage_free(temp)
698
699
return self._walsh_hadamard_transform
700
701
def absolute_walsh_spectrum(self):
702
"""
703
Return the absolute Walsh spectrum fo the function.
704
705
EXAMPLES::
706
707
sage: from sage.crypto.boolean_function import BooleanFunction
708
sage: B = BooleanFunction("7969817CC5893BA6AC326E47619F5AD0")
709
sage: sorted([_ for _ in B.absolute_walsh_spectrum().iteritems() ])
710
[(0, 64), (16, 64)]
711
712
sage: B = BooleanFunction("0113077C165E76A8")
713
sage: B.absolute_walsh_spectrum()
714
{8: 64}
715
"""
716
d = {}
717
for i in self.walsh_hadamard_transform():
718
if d.has_key(abs(i)):
719
d[abs(i)] += 1
720
else:
721
d[abs(i)] = 1
722
return d
723
724
def is_balanced(self):
725
"""
726
Return True if the function takes the value True half of the time.
727
728
EXAMPLES::
729
730
sage: from sage.crypto.boolean_function import BooleanFunction
731
sage: B = BooleanFunction(1)
732
sage: B.is_balanced()
733
False
734
sage: B[0] = True
735
sage: B.is_balanced()
736
True
737
"""
738
return self.walsh_hadamard_transform()[0] == 0
739
740
def is_symmetric(self):
741
"""
742
Return True if the function is symmetric, i.e. invariant under
743
permutation of its input bits. Another way to see it is that the
744
output depends only on the Hamming weight of the input.
745
746
EXAMPLES::
747
748
sage: from sage.crypto.boolean_function import BooleanFunction
749
sage: B = BooleanFunction(5)
750
sage: B[3] = 1
751
sage: B.is_symmetric()
752
False
753
sage: V_B = [0, 1, 1, 0, 1, 0]
754
sage: for i in srange(32): B[i] = V_B[i.popcount()]
755
sage: B.is_symmetric()
756
True
757
"""
758
cdef list T = [ self(2**i-1) for i in range(self._nvariables+1) ]
759
for i in xrange(2**self._nvariables):
760
if T[ hamming_weight_int(i) ] != bitset_in(self._truth_table, i):
761
return False
762
return True
763
764
def nonlinearity(self):
765
"""
766
Return the nonlinearity of the function. This is the distance
767
to the linear functions, or the number of output ones need to
768
change to obtain a linear function.
769
770
EXAMPLE::
771
772
sage: from sage.crypto.boolean_function import BooleanFunction
773
sage: B = BooleanFunction(5)
774
sage: B[1] = B[3] = 1
775
sage: B.nonlinearity()
776
2
777
sage: B = BooleanFunction("0113077C165E76A8")
778
sage: B.nonlinearity()
779
28
780
"""
781
if self._nonlinearity is None:
782
self._nonlinearity = ( (1<<self._nvariables) - max( [abs(w) for w in self.walsh_hadamard_transform()] ) ) >> 1
783
return self._nonlinearity
784
785
def is_bent(self):
786
"""
787
Return True if the function is bent.
788
789
EXAMPLE::
790
791
sage: from sage.crypto.boolean_function import BooleanFunction
792
sage: B = BooleanFunction("0113077C165E76A8")
793
sage: B.is_bent()
794
True
795
"""
796
if (self._nvariables & 1):
797
return False
798
return self.nonlinearity() == ((1<<self._nvariables)-(1<<(self._nvariables//2)))>>1
799
800
def correlation_immunity(self):
801
"""
802
Return the maximum value `m` such that the function is
803
correlation immune of order `m`.
804
805
A Boolean function is said to be correlation immune of order
806
`m` , if the output of the function is statistically
807
independent of the combination of any m of its inputs.
808
809
EXAMPLES::
810
811
sage: from sage.crypto.boolean_function import BooleanFunction
812
sage: B = BooleanFunction("7969817CC5893BA6AC326E47619F5AD0")
813
sage: B.correlation_immunity()
814
2
815
"""
816
cdef int c
817
if self._correlation_immunity is None:
818
c = self._nvariables
819
W = self.walsh_hadamard_transform()
820
for 0 < i < len(W):
821
if (W[i] != 0):
822
c = min( c , hamming_weight_int(i) )
823
self._correlation_immunity = ZZ(c-1)
824
return self._correlation_immunity
825
826
def resiliency_order(self):
827
"""
828
Return the maximum value `m` such that the function is
829
resilient of order `m`.
830
831
A Boolean function is said to be resilient of order `m` if it
832
is balanced and correlation immune of order `m`.
833
834
If the function is not balanced, we return -1.
835
836
EXAMPLES::
837
838
sage: from sage.crypto.boolean_function import BooleanFunction
839
sage: B = BooleanFunction("077CE5A2F8831A5DF8831A5D077CE5A26996699669699696669999665AA5A55A")
840
sage: B.resiliency_order()
841
3
842
"""
843
if not self.is_balanced():
844
return -1
845
return self.correlation_immunity()
846
847
def autocorrelation(self):
848
r"""
849
Return the autocorrelation fo the function, defined by
850
851
.. math:: \Delta_f(j) = \sum_{i\in\{0,1\}^n} (-1)^{f(i)\oplus f(i\oplus j)}.
852
853
EXAMPLES::
854
855
sage: from sage.crypto.boolean_function import BooleanFunction
856
sage: B = BooleanFunction("03")
857
sage: B.autocorrelation()
858
(8, 8, 0, 0, 0, 0, 0, 0)
859
"""
860
cdef long *temp
861
862
if self._autocorrelation is None:
863
n = self._truth_table.size
864
temp = <long *>sage_malloc(sizeof(long)*n)
865
W = self.walsh_hadamard_transform()
866
867
for 0<= i < n:
868
temp[i] = W[i]*W[i]
869
870
walsh_hadamard(temp, self._nvariables)
871
self._autocorrelation = tuple( [temp[i]>>self._nvariables for i in xrange(n)] )
872
sage_free(temp)
873
874
return self._autocorrelation
875
876
def absolute_autocorrelation(self):
877
"""
878
Return the absolute autocorrelation of the function.
879
880
EXAMPLES::
881
882
sage: from sage.crypto.boolean_function import BooleanFunction
883
sage: B = BooleanFunction("7969817CC5893BA6AC326E47619F5AD0")
884
sage: sorted([ _ for _ in B.absolute_autocorrelation().iteritems() ])
885
[(0, 33), (8, 58), (16, 28), (24, 6), (32, 2), (128, 1)]
886
"""
887
d = {}
888
for i in self.autocorrelation():
889
if d.has_key(abs(i)):
890
d[abs(i)] += 1
891
else:
892
d[abs(i)] = 1
893
return d
894
895
def absolut_indicator(self):
896
"""
897
Return the absolut indicator of the function. Ths is the maximal absolut
898
value of the autocorrelation.
899
900
EXAMPLES::
901
902
sage: from sage.crypto.boolean_function import BooleanFunction
903
sage: B = BooleanFunction("7969817CC5893BA6AC326E47619F5AD0")
904
sage: B.absolut_indicator()
905
32
906
"""
907
if self._absolut_indicator is None:
908
D = self.autocorrelation()
909
self._absolut_indicator = max([ abs(a) for a in D[1:] ])
910
return self._absolut_indicator
911
912
def sum_of_square_indicator(self):
913
"""
914
Return the sum of square indicator of the function.
915
916
EXAMPLES::
917
918
sage: from sage.crypto.boolean_function import BooleanFunction
919
sage: B = BooleanFunction("7969817CC5893BA6AC326E47619F5AD0")
920
sage: B.sum_of_square_indicator()
921
32768
922
"""
923
if self._sum_of_square_indicator is None:
924
D = self.autocorrelation()
925
self._sum_of_square_indicator = sum([ a**2 for a in D ])
926
return self._sum_of_square_indicator
927
928
def annihilator(self,d, dim = False):
929
r"""
930
Return (if it exists) an annihilator of the boolean function of
931
degree at most `d`, that is a Boolean polynomial `g` such that
932
933
.. math::
934
935
f(x)g(x) = 0 \forall x.
936
937
INPUT:
938
939
- ``d`` -- an integer;
940
- ``dim`` -- a Boolean (default: False), if True, return also
941
the dimension of the annihilator vector space.
942
943
EXAMPLES::
944
945
sage: from sage.crypto.boolean_function import BooleanFunction
946
sage: f = BooleanFunction("7969817CC5893BA6AC326E47619F5AD0")
947
sage: f.annihilator(1) is None
948
True
949
sage: g = BooleanFunction( f.annihilator(3) )
950
sage: set([ fi*g(i) for i,fi in enumerate(f) ])
951
set([0])
952
"""
953
# NOTE: this is a toy implementation
954
from sage.rings.polynomial.pbori import BooleanPolynomialRing
955
R = BooleanPolynomialRing(self._nvariables,'x')
956
G = R.gens()
957
r = [R(1)]
958
959
from sage.modules.all import vector
960
s = vector(self.truth_table()).support()
961
962
from sage.combinat.combination import Combinations
963
from sage.misc.misc import prod
964
965
from sage.matrix.constructor import Matrix
966
from sage.rings.arith import binomial
967
M = Matrix(GF(2),sum([binomial(self._nvariables,i) for i in xrange(d+1)]),len(s))
968
969
for i in xrange(1,d+1):
970
C = Combinations(self._nvariables,i)
971
for c in C:
972
r.append(prod([G[i] for i in c]))
973
974
cdef BooleanFunction t
975
976
for i,m in enumerate(r):
977
t = BooleanFunction(m)
978
for j,v in enumerate(s):
979
M[i,j] = bitset_in(t._truth_table,v)
980
981
kg = M.kernel().gens()
982
983
if len(kg)>0:
984
res = sum([kg[0][i]*ri for i,ri in enumerate(r)])
985
else:
986
res = None
987
988
if dim:
989
return res,len(kg)
990
else:
991
return res
992
993
def algebraic_immunity(self, annihilator = False):
994
"""
995
Returns the algebraic immunity of the Boolean function. This is the smallest
996
integer `i` such that there exists a non trivial annihilator for `self` or `~self`.
997
998
INPUT:
999
1000
- annihilator - a Boolean (default: False), if True, returns also an annihilator of minimal degree.
1001
1002
EXAMPLES::
1003
1004
sage: from sage.crypto.boolean_function import BooleanFunction
1005
sage: R.<x0,x1,x2,x3,x4,x5> = BooleanPolynomialRing(6)
1006
sage: B = BooleanFunction(x0*x1 + x1*x2 + x2*x3 + x3*x4 + x4*x5)
1007
sage: B.algebraic_immunity(annihilator=True)
1008
(2, x0*x1 + x1*x2 + x2*x3 + x3*x4 + x4*x5 + 1)
1009
sage: B[0] +=1
1010
sage: B.algebraic_immunity()
1011
2
1012
1013
sage: R.<x> = GF(2^8,'a')[]
1014
sage: B = BooleanFunction(x^31)
1015
sage: B.algebraic_immunity()
1016
4
1017
"""
1018
f = self
1019
g = ~self
1020
for i in xrange(self._nvariables):
1021
for fun in [f,g]:
1022
A = fun.annihilator(i)
1023
if A is not None:
1024
if annihilator:
1025
return i,A
1026
else:
1027
return i
1028
raise ValueError, "you just found a bug!"
1029
1030
def __setitem__(self, i, y):
1031
"""
1032
Set a value of the function.
1033
1034
EXAMPLE::
1035
1036
sage: from sage.crypto.boolean_function import BooleanFunction
1037
sage: B=BooleanFunction([0,0,1,1])
1038
sage: B[0]=1
1039
sage: B[2]=(3**17 == 9)
1040
sage: [b for b in B]
1041
[True, False, False, True]
1042
1043
We take care to clear cached values::
1044
1045
sage: W = B.walsh_hadamard_transform()
1046
sage: B[2] = 1
1047
sage: B._walsh_hadamard_transform_cached() is None
1048
True
1049
"""
1050
self._clear_cache()
1051
bitset_set_to(self._truth_table, int(i), int(y)&1)
1052
1053
def __getitem__(self, i):
1054
"""
1055
Return the value of the function for the given input.
1056
1057
EXAMPLE::
1058
1059
sage: from sage.crypto.boolean_function import BooleanFunction
1060
sage: B=BooleanFunction([0,1,1,1])
1061
sage: [ int(B[i]) for i in range(len(B)) ]
1062
[0, 1, 1, 1]
1063
"""
1064
return self.__call__(i)
1065
1066
def _clear_cache(self):
1067
"""
1068
Clear cached values.
1069
1070
EXAMPLE::
1071
1072
sage: from sage.crypto.boolean_function import BooleanFunction
1073
sage: B = BooleanFunction([0,1,1,0])
1074
sage: W = B.walsh_hadamard_transform()
1075
sage: B._walsh_hadamard_transform_cached() is None
1076
False
1077
sage: B._clear_cache()
1078
sage: B._walsh_hadamard_transform_cached() is None
1079
True
1080
"""
1081
self._walsh_hadamard_transform = None
1082
self._nonlinearity = None
1083
self._correlation_immunity = None
1084
self._autocorrelation = None
1085
self._absolut_indicator = None
1086
self._sum_of_square_indicator = None
1087
1088
def __reduce__(self):
1089
"""
1090
EXAMPLE::
1091
1092
sage: from sage.crypto.boolean_function import BooleanFunction
1093
sage: B = BooleanFunction([0,1,1,0])
1094
sage: loads(dumps(B)) == B
1095
True
1096
"""
1097
return unpickle_BooleanFunction, (self.truth_table(format='hex'),)
1098
1099
def unpickle_BooleanFunction(bool_list):
1100
"""
1101
Specific function to unpickle Boolean functions.
1102
1103
EXAMPLE::
1104
1105
sage: from sage.crypto.boolean_function import BooleanFunction
1106
sage: B = BooleanFunction([0,1,1,0])
1107
sage: loads(dumps(B)) == B # indirect doctest
1108
True
1109
"""
1110
return BooleanFunction(bool_list)
1111
1112
cdef class BooleanFunctionIterator:
1113
cdef long index, last
1114
cdef BooleanFunction f
1115
1116
def __init__(self, f):
1117
"""
1118
Iterator through the values of a Boolean function.
1119
1120
EXAMPLE::
1121
1122
sage: from sage.crypto.boolean_function import BooleanFunction
1123
sage: B = BooleanFunction(3)
1124
sage: type(B.__iter__())
1125
<type 'sage.crypto.boolean_function.BooleanFunctionIterator'>
1126
"""
1127
self.f = f
1128
self.index = -1
1129
self.last = self.f._truth_table.size-1
1130
1131
def __iter__(self):
1132
"""
1133
Iterator through the values of a Boolean function.
1134
1135
EXAMPLE::
1136
1137
sage: from sage.crypto.boolean_function import BooleanFunction
1138
sage: B = BooleanFunction(1)
1139
sage: [b for b in B] # indirect doctest
1140
[False, False]
1141
"""
1142
return self
1143
1144
def __next__(self):
1145
"""
1146
Next value.
1147
1148
EXAMPLE::
1149
1150
sage: from sage.crypto.boolean_function import BooleanFunction
1151
sage: B = BooleanFunction(1)
1152
sage: I = B.__iter__()
1153
sage: I.next()
1154
False
1155
"""
1156
if self.index == self.last:
1157
raise StopIteration
1158
self.index += 1
1159
return bitset_in(self.f._truth_table, self.index)
1160
1161
##########################################
1162
# Below we provide some constructions of #
1163
# cryptographic Boolean function. #
1164
##########################################
1165
1166
def random_boolean_function(n):
1167
"""
1168
Returns a random Boolean function with `n` variables.
1169
1170
EXAMPLE::
1171
1172
sage: from sage.crypto.boolean_function import random_boolean_function
1173
sage: B = random_boolean_function(9)
1174
sage: B.nvariables()
1175
9
1176
sage: B.nonlinearity()
1177
217 # 32-bit
1178
222 # 64-bit
1179
"""
1180
from sage.misc.randstate import current_randstate
1181
r = current_randstate().python_random()
1182
cdef BooleanFunction B = BooleanFunction(n)
1183
cdef bitset_t T
1184
T[0] = B._truth_table[0]
1185
for 0 <= i < T.limbs:
1186
T.bits[i] = r.randrange(0,Integer(1)<<(sizeof(unsigned long)*8))
1187
return B
1188
1189