Boolean Functions


Introduction


This page was created to exchange functions and computations developed in Sage by A. Bapić for our research on Boolean functions.

The following lines need to be added to your Sage notebook to support cryptographical functions that are available (for more info see Sage-BooleanFunctions and Sage-SBox).

from sage.crypto.sbox import SBox
from sage.crypto.boolean_function import BooleanFunction

Basic tools


Below we give some basic tools which will be used going onwards, including:

  • Walsh support of a Boolean function fBn
  • The inverse WHT given a Walsh spectra w of length 2n
  • Checking wheather a Boolean function f in odd dimension is semi-bent
  • Walsh spectra of a vectorial Boolean function F
  • Checking whether an (n,m)-function F is AB, if F is defined by a list of its coordinates (If F is given in integer form, there is already an integrated function in Sage)
def walsh_support(f,n):
    w=BooleanFunction(f).walsh_hadamard_transform();
    l=len(w);
    V=VectorSpace(GF(2),n);
    ws=[];
    for v in V:
        i=ZZ(list(v),base=2);
        if(w[i] !=0):
            ws.append(vector(GF(2),v));
    return ws;

def inverseWHT(w,n):
    t=[];
    V=sorted(VectorSpace(GF(2),n));
    for u in V:
        s=0;
        for x in V:
            i=ZZ(list(x), base=2);
            s=s+w[i]*(-1)^(vector(u,GF(2))*vector(x,GF(2)));
        t.append((1-(2^(-n))*s)/2);
    return t;


def is_semibent(f,n):
    if((f.is_plateaued()) and (2^((n+1)/2) in f.walsh_hadamard_transform())):
        return True;
    else:
        return False;

def walsh_spectrum(F):
    n=len(F);
    S=[];
    for i in [1..2^n-1]:
        f=F.component_function(i);
        S=S+list(f.walsh_hadamard_transform());
    K=sorted(list(set(S)));
    return [[K[i],S.count(K[i])] for i in [0..len(K)-1]];
    
def isAB(M):
    T=list(transpose(matrix(M)));
    T1=[ZZ(list(x),base=2) for x in T];
    T1=SBox(T1);
    return T1.is_almost_bent();


C and D classes


The Maiorana-McFarland class M is the set of m-variable (m=2n) Boolean functions of the form

f(x,y)=xπ(y)+g(y),oxforallx,yF2n,

where π is a permutation on F2n, and g is an arbitrary Boolean function on F2n. From this class Carlet derived the C class of bent functions that contains all functions of the form

f(x,y)=xπ(y)+1L(x)

where L is any linear subspace of F2n, 1L is the indicator function of the space L, and π is any permutation on F2n such that:

(C) ϕ(a+L) is a flat (affine subspace), for all aF2n, where ϕ:=π1.

The permutation ϕ and the subspace L are then said to satisfy the (C) property, or for short _(ϕ,L) has property (C)_.

Another class introduced by Carlet, called D, is defined similarly as f(x,y)=xπ(y)+1E1(x)1E2(y)

where π is a permutation on F2n and E1,E2 two linear subspaces of F2n such that π(E2)=E1.

We will define three Sage functions:

  • the indicator function 1A(x) which will be denoted with ind(x,A),
  • the trace function Trmn(x) denoted with tr(x,m,n) and
  • the anihilator of a linear space E of F2m denoted with dualSpace(E,m).
#Fm=GF(2^m);
def ind(x,L):
    if x in L:
        return Fm.one();
    else:
        return Fm.zero();


def tr(x,m,n):
    return sum([x^(2^(i*m)) for i in [0..(n/m)-1]])


def dualSpace(E,m):
    Fm=GF(2^m);
    t=[];
    for x in Fm:
        b=0;
        for y in E:
            if (x*y).trace()==0:
                b=b+1;
        if b==len(E):
            t.append(x)
    return t;

Example: Let m=9 and s=3. Then m/s is odd. Furthermore, for d=284 we have that d(2s+1)mod(291)=1. Let U={1,α,α2} where α is a primitive element of F23 such that α3+α+1=0.

For L=U we have that (π1,L) satisfies the (C) property. Hence, the function f:F2m×F2mF2 defined with f(x,y)=xπ(y)+1L(x) is bent.

Fm=GF(2^9);
sFm=sorted(Fm);

p=Fm.primitive_element();
a=p^((2^9-1)/(2^3-1));
u=sFm[13];

Lperp=[x for x in Fm if ((x).trace()+1)*((x*a).trace()+1)==1];

f=[(x*y^284).trace()+ind(x,Lperp) for x in sFm for y in sFm];
f=BooleanFunction(f);
f.is_bent()

Example: Let m=9 and s=3. Then m/s is odd. Furthermore, for d=284 we have that d(2s+1)mod(291)=1. Let α be a primitive element of F23 such that α3+α+1=0.

For E2=α,α2 we have that π(E2)=E2=E1. Hence, the function f:F2m×F2mF2 defined with f(x,y)=xπ(y)+1E1(x)1E2(y) is bent.

Fm=GF(2^9);
sFm=sorted(Fm);

p=Fm.primitive_element();
a=p^((2^9-1)/(2^3-1));

E2=[Fm.zero(),a^2,a+a^2,a];
E1=dualSpace(E2,9);

f=[(x*y^284).trace()+ind(x,E1)*ind(y,E2) for x in sFm for y in sFm];
f=BooleanFunction(f);
f.is_bent()

Let π be a permutation on F2m, LF2m be a linear subspace of F2m such that (π1,L) satisfies the (C) property, and let E1,E2{0} be two linear subspaces of F2m such that π(E2)=E1. Then the class of bent functions f:F2m×F2mF2 containing all functions of the form
f(x,y)=Tr1m(xπ(y))+a01L(x)+a11E1(x)1E2(y),,;aiF2 is called CD and is a superclass of C and D.

Example: Let m=9 and s=3. Then m/s is odd. Furthermore, for d=284 we have that d(2s+1)mod(291)=1. Let U={1,α,α2} where α is a primitive element of F23 such that α3+α+1=0.

For L=U we have that (π1,L) satisfies the (C) property. For E2=α,α2 we have that π(E2)=E2=E1. Hence, the function f:F2m×F2mF2 defined with f(x,y)=xπ(y)+1L(x)+1E1(x)1E2(y) is bent.

Below we give a program (depending if we are using vector space or finite field notation), which checks if (π1,L) satisfies the (C) property. The permutations pi_inv are given in their integer representation.

def shift_space(W,u):
    new=[];
    for v in W:
        new.append(vector(GF(2),v)+vector(GF(2),u));
    return new;

def is_flat(A,n):
    V=VectorSpace(GF(2),n);
    A1=V.subspace(A);
    if len(list(A1))==len(A):
        return True;
    else:
        return False;

def is_closed(F):
    C=Combinations([0..len(F)-1],2);
    C=C.list();
    G=Graph();
    for i in C:
        if F[i[0]]+F[i[1]] in F:
            G.add_edge([i[0],i[1]])
    if(len(G.clique_maximum())==len(F)):
        return True;
    else:
        return False;

def satisfiesC(pi_inv,L,n):
    t=[];
    Fn=GF(2^n);
    Vn=Fn.vector_space();
    for a in Vn:
        A=shift_space(L,a);
        F=[Vn(pi_inv[ZZ(list(x),base=2)]) for x in A];
        t.append[is_flat(F,n)];
    return t;

def satisfiesC_field(pi_inv,L,n,F):
    Fk=F;
    for a in Fk:
        if a!=Fk.zero():
            A=[pi_inv(a+L[i]) for i in [0..3]];
            B=[A[0]+A[i] for i in [0..3]];
            if (0 in B) and (is_closed(B)):
                return True;
            else:
                return False;

Inclusion/exclusion in M#

A bent function f in n variables belongs to M# if and only if there exists an n2-dimensional linear subspace V of F2n such that the second-order derivatives DaDbf(x)=f(x)f(xa)f(xb)f(xab) vanish for any a,bV.

def is_in_MM(f,n):
    s=[];
    for a in [1..2^n-1]:
        for b in [a+1..2^n-1]:
            if set(ttab(f.derivative(a).derivative(b)))=={0}:
                s.append([a,b]);
    G=Graph();
    G.add_edges(s);
    cl=list(sage.graphs.cliquer.all_cliques(G,2^(n/2)-1,2^(n/2)-1));
    V=VectorSpace(GF(2),n);
    V1=sorted(V);
    b1=[V.subspace([V1[0]]+[V1[i] for i in s]) for s in cl];
    for K in b1:
        if len(K)==2^(n/2):
            return True;
    return False;

Example: The function fD0 defined by f(x,y)=Tr14(xy7)+δ0(x) for x,yF24 is a bent function outside M#.

Fm=GF(2^4);
sFm=sorted(Fm);

f=[(x*y^7).trace()+ind(x,[0]) for x in sFm for y in sFm];
f=BooleanFunction(f);
is_in_MM(f,8)
False

Duals of Boolean function

Depending on wheather f is a bent or plateaued function, we have different notions of duals.

For a bent Boolean function f defined on F2n, its dual f is defined as a function from F2n to F2, for which it holds that (1)f(x)=2n2Wf(x),  xF2n. A standard way of defining the dual f~:F2nF2 of an s-plateaued Boolean function f on F2n is as follows: f~(x)=2n+s2|Wf(x)|,  xF2n.

def bent_dual(f,n): # f is bent
    t=[];
    w=f.walsh_hadamard_transform();
    for x in w:
        if x==2^(n/2):
            t.append(0);
        else:
            t.append(1);
    return BooleanFunction(t);

def stand_dual(f,n): # f is s-plateaued
    t=[];
    w=BooleanFunction(f).walsh_hadamard_transform();
    for x in w:
        if x==0:
            t.append(0);
        else:
            t.append(1);
    return BooleanFunction(t);

Let Sf=vE={ω0,,ω2ks1}F2k, for some vF2k and linear subspace E={e0,e1,,e2ks1}F2k. For a function f:F2ksF2 with wt(f)=2ks1±2ks21, let the Walsh spectrum of f be defined (by identifying xiF2ks and eiE) as Wf(u)=2k+s2(1)f(xi) for u=veiSf, and Wf(u)=0 otherwise. Then f is an s-plateaued function if and only if f is a bent function on F2ks.

def plateFromBent(S,ttD,s,n):
    ws=[0 for i in [0..(2^n)-1]];
    j=0;
    for x in S:
        i=ZZ(list(x), base=2);
        ws[i]=2^((n+s)/2)*((-1)^(int(ttD[j])));
        j=j+1;
    return inverseWHT(ws);

(PU) property

We say that a Boolean function gBn satisfies the property (PU) with the defining set UF2n if DuDvg0 for any u,vU. The program below finds the largest set U for which this property is satisfied.

def Property_PTau(g,n):
    FF=sorted(GF(2^n));
    l=[sorted([x,y]) for x in [1..2^n-1] for y in [x+1..2^n-1] if g.derivative(x).derivative(y).algebraic_degree()==-1];
    G=Graph();
    G.add_edges(l);
    return G.cliques_maximum();

Functions φu and ψu

For the definition and application of the functions φu and ψu we reffer to the following article of BPH - CLICK HERE.

def phi(u,fd,gd,Sf,Sg):
    L=[s for s in Sf if s not in intersection(Sf,Sg)];
    tt=[];
    for x in L:
        i=Sf.index(x);
        a=fd[i];
        v=vector(GF(2),u)+vector(GF(2),x);
        j=Sg.index(v);
        b=gd[j];
        tt.append((a+b)%2);
    return tt;

def psi(u,fd,gd,Sf,Sg):
    L1=intersection(Sf,Sg);
    tt=[];
    for x in L1:
        i=Sf.index(x);
        a=fd[i];
        v=vector(GF(2),u)+vector(GF(2),x);
        j=Sg.index(v);
        b=gd[j];
        tt.append((a+b)%2);
    return tt;

Constructions of 4-bent decompositions outside M#

Let gM# be a bent function in n variables. For an arbitrary matrix MGL(n,F2) and vector cF2n, let Sf=(cF2nM)Tt1Tt2, where t1,t2Bn such that g(x,y)v1t1(x,y)v2t2(x,y) is bent for any v1,v2F2, where x,yF2n/2. Let Q={0n}×F22={q0,q1,q2,q3} and set Sfi=qiSf, for i=0,,3. Then, the functions fiBn+2, constructed using Theorem 3.3 in HPWZ, with Sfi and g, are semi-bent functions on F2n+2 with pairwise disjoint spectra. Moreover, the function fBn+4, whose restrictions are f|aiF2n+4=fi (thus f=f1||f2||f3||f4), where ai0n+2×F22, is a bent function outside M#.

Example: Below is an example in dimension 8 for t1=t2=δ0 and gD0M#.

def concat(tt):
    s=[];
    for x in tt:
        s=s+x;
    return BooleanFunction(s)

def delta(x):
    if x==0:
        return 1;
    else:
        return 0;


n=8;
G=GL(n,GF(2));
M=G.random_element();
E=VectorSpace(GF(2),n);
c=E.random_element();
S1=[c+M*x for x in E];
t1=[delta(x) for x in sorted(GF(2^4)) for y in sorted(GF(2^4))];

Sf=[list(S1[i])+[t1[i],t1[i]] for i in [0..255]]
Sf2=[vector(GF(2),x)+vector([0,0,0,0,0,0,0,0,0,1]) for x in Sf]
Sf3=[vector(GF(2),x)+vector([0,0,0,0,0,0,0,0,1,0]) for x in Sf]
Sf4=[vector(GF(2),x)+vector([0,0,0,0,0,0,0,0,1,1]) for x in Sf]
SF=[Sf,Sf2,Sf3,Sf4];

F=[];
g=[(x*y^7).trace()+delta(x) for x in sorted(GF(2^4)) for y in sorted(GF(2^4))]
f1=[(-1)^(g[i]) for i in [0..len(g)-1]];
S1=[[ZZ(list(x),base=2) for x in A] for A in SF]
for s1 in S1:
    t=[0 for i in [0..2^(10)-1]];
    j=0;
    for i in s1:
        t[i]=(2^((n+4)/2)*f1[j]);
        j=j+1;
    F.append(inverseWHT(t,10));

G=concat(F);
G=BooleanFunction(G);