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