Ticket #681: mq.patch

File mq.patch, 85.6 KB (added by malb, 2 years ago)
  • sage/all.py

    # HG changeset patch
    # User Martin Albrecht <malb@informatik.uni-bremen.de>
    # Date 1190044032 -3600
    # Node ID 793d40d163d73409d286a0b5ccbab400657ffd7c
    # Parent  fb0db5af8af06151673d813e9c2f0ce288b30a25
    introducing MQ module. It is meant as a module to support research in algebraic cryptanalysis
    of (block) ciphers. Right now, an MPolynomialSystem class is implemented and a generator for
    small scale AES polynomial systems.
    
    diff -r fb0db5af8af0 -r 793d40d163d7 sage/all.py
    a b  
    8383import sage.tests.all as tests 
    8484 
    8585from sage.crypto.all     import * 
     86import sage.crypto.mq as mq 
    8687 
    8788from sage.plot.all       import * 
    8889from sage.coding.all     import * 
  • (a) /dev/null vs. (b) b/sage/crypto/mq/__init__.py

    diff -r fb0db5af8af0 -r 793d40d163d7 sage/crypto/mq/__init__.py
    a b  
     1from mpolynomialsystem import MPolynomialSystem, MPolynomialRoundSystem 
     2from sr import SR 
  • (a) /dev/null vs. (b) b/sage/crypto/mq/mpolynomialsystem.py

    diff -r fb0db5af8af0 -r 793d40d163d7 sage/crypto/mq/mpolynomialsystem.py
    a b  
     1""" 
     2Multivariate Polynomial System. 
     3 
     4We call a finite set of multivariate polynomials an MPolynomialSystem. 
     5 
     6Furthermore we assume that these multivariate polynomials have a 
     7common solution if interpreted as equations where the left hand side 
     8is the polynomial and the right hand side is equal to zero. Or in 
     9other terms: The set of multivariate polynomials have common roots. In 
     10many other computer algebra systems this class could be called Ideal 
     11but -- strictly speaking -- an ideal is a very distinct object form its 
     12generators and thus this is not an Ideal in SAGE. 
     13 
     14The main purpose of this class is to manipulate an MPolynomialSystem 
     15to gather the common solution. 
     16 
     17This idea is specialized to an MPolynomialSystem which consists of 
     18several rounds. These kind of polynomial systems are often found in 
     19symmetric algebraic cryptanalysis. The most prominent examples of these 
     20kind of systems are: SR (AES), Flurry/Curry, and CTC(2). 
     21 
     22AUTHOR: Martin Albrecht <malb@informatik.uni-bremen.de> 
     23""" 
     24 
     25from sage.structure.sage_object import SageObject 
     26from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing 
     27from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal 
     28from sage.rings.polynomial.multi_polynomial import is_MPolynomial 
     29 
     30from sage.rings.integer_ring import ZZ 
     31 
     32from sage.matrix.matrix import is_Matrix 
     33from sage.matrix.constructor import Matrix 
     34 
     35from sage.misc.misc import uniq 
     36 
     37def is_MPolynomialSystem(F): 
     38    """ 
     39    Return True if F is an MPolynomialSystem 
     40    """ 
     41    return isinstance(F,MPolynomialSystem_generic) 
     42 
     43def is_MPolynomialRoundSystem(F): 
     44    """ 
     45    Return True if F is an MPolynomialRoundSystem 
     46    """ 
     47    return isinstance(F,MPolynomialRoundSystem_generic) 
     48 
     49 
     50def MPolynomialRoundSystem(R, gens): 
     51    """ 
     52    Construct an object representing the equations of a single 
     53    round (e.g. of a block cipher). 
     54 
     55    INPUT: 
     56        R -- base ring 
     57        gens -- list (default: []) 
     58 
     59    EXAMPLE: 
     60        sage: P.<x,y,z> = MPolynomialRing(GF(2),3) 
     61        sage: mq.MPolynomialRoundSystem(P,[x*y +1, z + 1]) 
     62        [x*y + 1, z + 1] 
     63    """ 
     64    return MPolynomialRoundSystem_generic(R, gens) 
     65 
     66def MPolynomialSystem(arg1, arg2=None): 
     67    """ 
     68    Construct a new MPolynomialSystem. 
     69 
     70    INPUT: 
     71        arg1 -- a multivariate polynomial ring or an ideal 
     72        arg2 -- an iterable object of rounds, preferable MPolynomialRoundSystem, 
     73                or polynomials (default:None) 
     74 
     75    EXAMPLES: 
     76        sage: P.<a,b,c,d> = PolynomialRing(GF(127),4) 
     77        sage: I = sage.rings.ideal.Katsura(P) 
     78 
     79        If a list of MPolynomialRoundSystems is provided those 
     80        form the rounds. 
     81         
     82        sage: mq.MPolynomialSystem(I.ring(), [mq.MPolynomialRoundSystem(I.ring(),I.gens())]) 
     83        Polynomial System with 4 Polynomials in 4 Variables 
     84 
     85        If an ideal is provided the generators are used. 
     86         
     87        sage: mq.MPolynomialSystem(I) 
     88        Polynomial System with 4 Polynomials in 4 Variables 
     89 
     90        If a list of polynomials is provided the system has only 
     91        one round. 
     92 
     93        sage: mq.MPolynomialSystem(I.ring(), I.gens()) 
     94        Polynomial System with 4 Polynomials in 4 Variables 
     95    """ 
     96    if is_MPolynomialRing(arg1): 
     97        R = arg1 
     98        rounds = arg2 
     99    elif isinstance(arg1,MPolynomialIdeal): 
     100        R = arg1.ring() 
     101        rounds = arg1.gens() 
     102    else: 
     103        raise TypeError, "first parameter must be a MPolynomialRing"             
     104 
     105    k = R.base_ring() 
     106 
     107    if k.characteristic() != 2: 
     108        return MPolynomialSystem_generic(R,rounds) 
     109    elif k.degree() == 1: 
     110        return MPolynomialSystem_gf2(R,rounds) 
     111    elif k.degree() > 1: 
     112        return MPolynomialSystem_gf2e(R,rounds) 
     113 
     114class MPolynomialRoundSystem_generic(SageObject): 
     115    """ 
     116    Represents a multivariate polynomial set e.g. of a single round of 
     117    a block cipher. 
     118    """ 
     119    def __init__(self, R, gens): 
     120        """ 
     121        Construct an object representing the equations of a single 
     122        round (e.g. of a block cipher). 
     123 
     124        INPUT: 
     125            R -- base ring 
     126            gens -- list (default: []) 
     127 
     128        EXAMPLE: 
     129            sage: P.<x,y,z> = MPolynomialRing(GF(2),3) 
     130            sage: mq.MPolynomialRoundSystem(P,[x*y +1, z + 1]) 
     131            [x*y + 1, z + 1] 
     132        """ 
     133        if is_MPolynomialRing(R): 
     134            self._ring = R 
     135        else: 
     136            raise TypeError, "first parameter must be a MPolynomialRing" 
     137 
     138        if is_Matrix(gens): 
     139            self._gens = gens.list() 
     140        else: 
     141            self._gens = list(gens) 
     142         
     143    def ring(self): 
     144        """ 
     145        Return the base ring. 
     146        """ 
     147        return self._ring 
     148 
     149    def ngens(self): 
     150        """ 
     151        Return number of polynomials in self. 
     152        """ 
     153        return len(self._gens) 
     154 
     155    def gens(self): 
     156        """ 
     157        Return list of polynomials in self. 
     158        """ 
     159        return list(self) 
     160 
     161 
     162    def ideal(self): 
     163        """ 
     164        Return the ideal spanned by the polynomials in self. 
     165        """ 
     166        return self._ring.ideal(self.gens()) 
     167 
     168    def variables(self): 
     169        """ 
     170        Return unordered list of variables apprearing in polynomials 
     171        in self. 
     172        """ 
     173        return uniq(sum([f.variables() for f in self._gens],[])) 
     174 
     175    def monomials(self): 
     176        """ 
     177        Return unordered list of monomials appearing in polynomials 
     178        in self. 
     179        """ 
     180        return uniq(sum([f.monomials() for f in self._gens],[])) 
     181 
     182    def subs(self, *args, **kwargs): 
     183        """ 
     184        Substitute variables for every polynomial in self. See 
     185        MPolynomial.subs for calling convention. 
     186 
     187        INPUT: 
     188            args -- arguments to be passed to MPolynomial.subs 
     189            kwargs -- keyword arguments to be passed to MPolynomial.subs 
     190        """ 
     191        for i in range(len(self._gens)): 
     192            self._gens[i] = self._gens[i].subs(*args,**kwargs) 
     193 
     194    def _repr_(self): 
     195        return "%s"%self._gens 
     196 
     197    def __getitem__(self, i): 
     198        return self._gens[i] 
     199 
     200    def __add__(self, right): 
     201        if is_MPolynomialRoundSystem(right) and self.ring() == right.ring(): 
     202            return MPolynomialRoundSystem(self.ring(), self._gens + right.gens()) 
     203        if isinstance(right, (list,tuple)) and self.ring() == right[0]: 
     204            return MPolynomialRoundSystem(self.ring(), self._gens + right) 
     205        else: 
     206            raise ArithmeticError, "Cannot add MPolynomialRoundSystem and %s"%type(right) 
     207 
     208    def __contains__(self, element): 
     209        return (element in self._gens) 
     210 
     211    def __list__(self): 
     212        return list(self._gens) 
     213 
     214    def __len__(self): 
     215        """ 
     216        Return self.ngens(). 
     217        """ 
     218        return len(self._gens) 
     219 
     220    def __iter__(self): 
     221        """ 
     222        Iterate over the polynomials of self. 
     223        """ 
     224        return iter(self._gens) 
     225 
     226    def _singular_(self): 
     227        """ 
     228        Return SINGULAR ideal representation of self. 
     229        """ 
     230        return singular.ideal(self._gens) 
     231 
     232    def _magma_(self): 
     233        """ 
     234        Return MAGMA ideal representation of self. 
     235        """ 
     236        return magma.ideal(self._gens) 
     237 
     238class MPolynomialSystem_generic(SageObject): 
     239    """ 
     240    A system of multivariate polynomials. That is, a set of 
     241    multivariate polynomials with at least one common root. 
     242    """ 
     243    def __init__(self, R, rounds): 
     244        """ 
     245        Construct a new MPolynomialSystem. 
     246 
     247        INPUT: 
     248            arg1 -- a multivariate polynomial ring or an ideal 
     249            arg2 -- an iterable object of rounds, preferable MPolynomialRoundSystem, 
     250                      or polynomials (default:None) 
     251 
     252        EXAMPLES: 
     253            sage: P.<a,b,c,d> = PolynomialRing(GF(127),4) 
     254            sage: I = sage.rings.ideal.Katsura(P) 
     255 
     256            If a list of MPolynomialRoundSystems is provided those 
     257            form the rounds. 
     258             
     259            sage: mq.MPolynomialSystem(I.ring(), [mq.MPolynomialRoundSystem(I.ring(),I.gens())]) 
     260            Polynomial System with 4 Polynomials in 4 Variables 
     261 
     262            If an ideal is provided the generators are used. 
     263             
     264            sage: mq.MPolynomialSystem(I) 
     265            Polynomial System with 4 Polynomials in 4 Variables 
     266 
     267            If a list of polynomials is provided the system has only 
     268            one round. 
     269 
     270            sage: mq.MPolynomialSystem(I.ring(), I.gens()) 
     271            Polynomial System with 4 Polynomials in 4 Variables 
     272        """ 
     273 
     274        self._ring = R 
     275        self._rounds = [] 
     276 
     277        # check for list of polynomials 
     278        e = iter(rounds).next() 
     279        if is_MPolynomial(e): 
     280            rounds = [rounds] 
     281         
     282        for b in rounds: 
     283            if not is_MPolynomialRoundSystem(b): 
     284                if isinstance(b, (tuple,list)): 
     285                    self._rounds.append(MPolynomialRoundSystem(R, b)) 
     286                else: 
     287                    self._rounds.append(MPolynomialRoundSystem(R, list(b))) 
     288            elif b.ring() is R or b.ring() == R: 
     289                self._rounds.append(b) 
     290            else: 
     291                raise TypeError, "parameter not supported" 
     292 
     293    def ring(self): 
     294        """ 
     295        Return base ring. 
     296        """ 
     297        return self._ring 
     298 
     299    def ngens(self): 
     300        """ 
     301        Return number polynomials in self 
     302        """ 
     303        return sum([e.ngens() for e in self._rounds]) 
     304 
     305    def gens(self): 
     306        """ 
     307        Return list of polynomials in self 
     308        """ 
     309        return list(self) 
     310 
     311    def gen(self, ij): 
     312        """ 
     313        Return an element of self. 
     314 
     315        INPUT: 
     316            ij -- tuple, slice, integer 
     317 
     318        EXAMPLES: 
     319            sage: P.<a,b,c,d> = PolynomialRing(GF(127),4) 
     320            sage: F = mq.MPolynomialSystem(sage.rings.ideal.Katsura(P)) 
     321 
     322            $ij$-th polynomial overall 
     323 
     324            sage: F[0] 
     325            a + 2*b + 2*c + 2*d - 1 
     326             
     327            $i$-th to $j$-th polynomial overall 
     328 
     329            sage: F[0:2] 
     330            [a + 2*b + 2*c + 2*d - 1, a^2 + 2*b^2 + 2*c^2 + 2*d^2 - a] 
     331 
     332            $i$-th round, $j$-th polynomial 
     333             
     334            sage: F[0,1] 
     335            a^2 + 2*b^2 + 2*c^2 + 2*d^2 - a 
     336        """ 
     337        return self[ij] 
     338 
     339    def nrounds(self): 
     340        """ 
     341        Return number of rounds of self. 
     342        """ 
     343        return len(self._rounds) 
     344 
     345    def rounds(self): 
     346        """ 
     347        Return list of rounds of self. 
     348        """ 
     349        return list(self._rounds) 
     350 
     351    def round(self, i): 
     352        """ 
     353        Return $i$-th round of self. 
     354        """ 
     355        return self._rounds[i] 
     356 
     357    def __iter__(self): 
     358        for b in self._rounds: 
     359            for e in b: 
     360                yield e 
     361 
     362    def ideal(self): 
     363        """ 
     364        Return SAGE ideal spanned by self.gens() 
     365        """ 
     366        return self._ring.ideal(self.gens()) 
     367 
     368    def groebner_basis(self, *args, **kwargs): 
     369        """ 
     370        Compute and return a  Groebner basis for self. 
     371 
     372        INPUT: 
     373            args -- list of arguments passed to MPolynomialIdeal.groebner_basis call 
     374            kwargs -- dictionary of arguments passed to MPolynomialIdeal.groebner_basis call 
     375        """ 
     376        return self.ideal().groebner_basis(*args, **kwargs) 
     377 
     378    def monomials(self): 
     379        """ 
     380        Return a list of monomials in self. 
     381        """ 
     382        return uniq(sum([r.monomials() for r in self._rounds],[])) 
     383 
     384    def nmonomials(self): 
     385        """ 
     386        Return the number of monomials present in self. 
     387        """ 
     388        return len(self.monomials()) 
     389 
     390    def variables(self): 
     391        """ 
     392        Return all variables present in self. This list may or may not 
     393        be equal to the generators of the ring of self. 
     394        """ 
     395        return uniq(sum([r.variables() for r in self._rounds],[])) 
     396 
     397    def nvariables(self): 
     398        """ 
     399        Return number of variables present in self. 
     400        """ 
     401        return len(self.variables()) 
     402 
     403    def coeff_matrix(self): 
     404        """ 
     405        Return tuple (A,v) where A is the coefficent matrix of self 
     406        and v the matching monomial vector. Monomials are order w.r.t. 
     407        the term ordering of self.ring() in reverse order. 
     408 
     409        EXAMPLE: 
     410            sage: P.<a,b,c,d> = PolynomialRing(GF(127),4) 
     411            sage: I = sage.rings.ideal.Katsura(P) 
     412            sage: I.gens() 
     413            (a + 2*b + 2*c + 2*d - 1, a^2 + 2*b^2 + 2*c^2 + 2*d^2 - a, 2*a*b + 2*b*c 
     414            + 2*c*d - b, b^2 + 2*a*c + 2*b*d - c) 
     415 
     416            sage: F = mq.MPolynomialSystem(I) 
     417            sage: A,v = F.coeff_matrix() 
     418            sage: A 
     419            [  0   0   0   0   0   0   0   0   0   1   2   2   2 126] 
     420            [  1   0   2   0   0   2   0   0   2 126   0   0   0   0] 
     421            [  0   2   0   0   2   0   0   2   0   0 126   0   0   0] 
     422            [  0   0   1   2   0   0   2   0   0   0   0 126   0   0] 
     423             
     424            sage: v 
     425            [a^2] 
     426            [a*b] 
     427            [b^2] 
     428            [a*c] 
     429            [b*c] 
     430            [c^2] 
     431            [b*d] 
     432            [c*d] 
     433            [d^2] 
     434            [  a] 
     435            [  b] 
     436            [  c] 
     437            [  d] 
     438            [  1] 
     439             
     440            sage: A*v 
     441            [        a + 2*b + 2*c + 2*d - 1] 
     442            [a^2 + 2*b^2 + 2*c^2 + 2*d^2 - a] 
     443            [      2*a*b + 2*b*c + 2*c*d - b] 
     444            [        b^2 + 2*a*c + 2*b*d - c] 
     445         
     446        """ 
     447        R = self.ring() 
     448 
     449        m = sorted(self.monomials(),reverse=True) 
     450        nm = len(m) 
     451        f = self.gens() 
     452        nf = len(f) 
     453 
     454        #construct dictionary for fast lookups 
     455        v = dict( zip( m , range(len(m)) ) ) 
     456 
     457        A = Matrix( R.base_ring(), nf, nm ) 
     458 
     459        for x in range( nf ): 
     460            poly = f[x] 
     461            for y in poly.monomials(): 
     462                A[ x , v[y] ] = poly.monomial_coefficient(y) 
     463 
     464        return  A, Matrix(R,nm,1,m) 
     465 
     466    def subs(self, *args, **kwargs): 
     467        """ 
     468        Substitute variables for every polynomial in self. See 
     469        MPolynomial.subs for calling convention. 
     470 
     471        INPUT: 
     472            args -- arguments to be passed to MPolynomial.subs 
     473            kwargs -- keyword arguments to be passed to MPolynomial.subs 
     474        """ 
     475        for r in self._rounds: 
     476            r.subs(*args,**kwargs) 
     477 
     478    def _singular_(self): 
     479        """ 
     480        Return SINGULAR ideal representation of self. 
     481        """ 
     482        return singular.ideal(list(self)) 
     483 
     484    def _magma_(self): 
     485        """ 
     486        Return MAGMA ideal representation of self. 
     487        """ 
     488        return magma.ideal(list(self)) 
     489 
     490    def _repr_(self): 
     491        return "Polynomial System with %d Polynomials in %d Variables"%(self.ngens(),self.nvariables()) 
     492 
     493    def __add__(self, right): 
     494        """ 
     495        Add polynomial systems together, i.e. create a union of their 
     496        polynomials. 
     497        """ 
     498        if is_MPolynomialRoundSystem(right) and right.ring() == self.ring(): 
     499            return MPolynomialSystem(self.ring(),self.rounds() + right.rounds()) 
     500        elif is_MPolynomialRoundSystem(right) and right.ring() == self.ring(): 
     501            return MPolynomialSystem(self.ring(),self.rounds() + [right.gens()]) 
     502        else: 
     503            raise TypeError, "right must be MPolynomialRing over same ring as self" 
     504 
     505    def __getitem__(self, ij): 
     506        """ 
     507        See self.gen(). 
     508        """ 
     509        if isinstance(ij, tuple): 
     510            i,j = ij 
     511            return self._rounds[i][j] 
     512        elif isinstance(ij, slice): 
     513            return sum(self._rounds,MPolynomialRoundSystem(self.ring(),[]))[ij] 
     514        else: 
     515            ij = int(ij) 
     516            for r in self._rounds: 
     517                if ij >= len(r): 
     518                    ij = ij - len(r) 
     519                else: 
     520                    return r[ij] 
     521 
     522    def __contains__(self, element): 
     523        """ 
     524        Return True if element is in self or False else. 
     525        """ 
     526        for r in self._rounds: 
     527            if element in r: 
     528                return True 
     529        return False 
     530 
     531    def __list__(self): 
     532        """ 
     533        Return a list of self where all polynomials in self are 
     534        presented in order as they appear in self. 
     535        """ 
     536        return sum([list(e) for e in self._rounds],[]) 
     537 
     538 
     539class MPolynomialSystem_gf2(MPolynomialSystem_generic): 
     540    """ 
     541    MPolynomialSystem over GF(2). 
     542    """ 
     543    def cnf(self): 
     544        """ 
     545        Return Canonical Normal Form (CNF) representation of self in a 
     546        format MiniSAT et al. can understand. 
     547        """ 
     548        raise NotImplemented 
     549 
     550class MPolynomialSystem_gf2e(MPolynomialSystem_generic): 
     551    r""" 
     552    MPolynomialSystem over GF(2^e). 
     553    """ 
     554 
     555    def change_ring(self, k): 
     556        """ 
     557        Project self onto $k$ 
     558 
     559        INPUT: 
     560            k -- GF(2) (parameter only  for compatible syntax) 
     561 
     562        NOTE: Based on SINGULAR implementation by Michael Brickenstein 
     563        <brickenstein@googlemail.com> 
     564 
     565        """ 
     566        R = self.ring() 
     567        nvars = R.ngens() 
     568        k = R.base_ring() 
     569   
     570        helper = PolynomialRing(GF(2), nvars + 1, [str(k.gen())] + map(str, R.gens()), order='lex') 
     571        myminpoly = helper(str(k.polynomial())) 
     572   
     573        l = map(lambda x: helper(str(x)), self.gens()) 
     574  
     575        r = myminpoly.degree() 
     576   
     577        intermediate_ring = PolynomialRing(GF(2), nvars*r+1, 'x', order='degrevlex') 
     578   
     579        a = intermediate_ring.gen(0) 
     580   
     581        # map e.g. x -> a^2*x_2 + a*x_1 + x_0, where x_0,..,x_2 represent 
     582        # the bits of x 
     583        map_ideal = [a] 
     584 
     585        var_index=0 
     586        for index in range(nvars): 
     587           _sum=0 
     588           for sum_index in range(r): 
     589              var_index += 1 
     590              _sum += a**sum_index * intermediate_ring.gen(var_index) 
     591           map_ideal.append(_sum) 
     592 
     593        myminpoly=myminpoly(*map_ideal) 
     594 
     595        l = [f(*map_ideal).reduce([myminpoly]) for f in l] 
     596 
     597        print map_ideal 
     598   
     599        result = [] 
     600        # split e.g. a^2*x0+a*x1+x2 to x0,x1,x2 
     601        for f in l: 
     602            for i in reversed(range(r)): 
     603               g = f.coefficient(a**i) 
     604               result.append(g) 
     605               f =  f - a**i * g 
     606   
     607        # eliminate parameter, change order to lp 
     608        new_var_names = [str(var)+"%d"%i for var in R.gens() for i in range(r)] 
     609   
     610        result_ring = PolynomialRing(GF(2), nvars*r,new_var_names, order=R.term_order()) 
     611   
     612        map_ideal = (0,) + result_ring.gens() 
     613        result = [f(*map_ideal) for f in result] 
     614        result += [e**2 + e for e in result_ring.gens()] 
     615   
     616        return MPolynomialSystem(result_ring,result) 
     617 
     618 
     619 
     620         
  • (a) /dev/null vs. (b) b/sage/crypto/mq/mpolynomialsystemgenerator.py

    diff -r fb0db5af8af0 -r 793d40d163d7 sage/crypto/mq/mpolynomialsystemgenerator.py
    a b  
     1""" 
     2Abstract base class for generators of MPolynomialSystems. 
     3 
     4AUTHOR: 
     5    Martin Albrecht <malb@informatik.uni-bremen.de> 
     6""" 
     7 
     8from sage.structure.sage_object import SageObject 
     9 
     10class MPolynomialSystemGenerator(SageObject): 
     11    """ 
     12    Abstract base class for generators of MPolynomialSystems. 
     13    """ 
     14 
     15    def __getattr__(self, attr): 
     16        if attr == "R": 
     17            self.R = self.ring() 
     18            return self.R 
     19        else: 
     20            raise AttributeError 
     21 
     22    def varformatstr(self, name): 
     23        """ 
     24        Return format string for a given name 'name' which is 
     25        understood by print et al. 
     26 
     27        Such a format string is used to construct variable 
     28        names. Typically those format strings are somewhat like 
     29        'name%02d%02d' such that rounds and offset in a block can be 
     30        encoded. 
     31 
     32        INPUT: 
     33            name -- string 
     34        """ 
     35        raise NotImplementedError 
     36         
     37    def varstrs(self, name, round): 
     38        """ 
     39        Return a list of variable names given a name 'name' and and 
     40        index 'round'. 
     41 
     42        This function is typically used by self._vars. 
     43 
     44        INPUT: 
     45            name -- string 
     46            round -- integer index 
     47        """ 
     48        raise NotImplementedError 
     49 
     50    def vars(self, name, round): 
     51        """ 
     52        Return a list of variables given a name 'name' and and 
     53        index 'round'. 
     54 
     55        INPUT: 
     56            name -- string 
     57            round -- integer index 
     58        """ 
     59        raise NotImplementedError 
     60 
     61    def ring(self): 
     62        """ 
     63        Return the ring in which the system is defined. 
     64        """ 
     65        raise NotImplementedError 
     66 
     67    def block_order(self): 
     68        """ 
     69        Return a block term ordering for the equation systems 
     70        generated by self. 
     71        """ 
     72        raise NotImplementedError 
     73 
     74    def __call__(self, P, K): 
     75        """ 
     76        Encrypt plaintext P using the key K. 
     77 
     78        INPUT: 
     79            P -- plaintext (vector, list) 
     80            K -- key (vector, list) 
     81        """ 
     82        raise NotImplementedError 
     83 
     84    def sbox(self): 
     85        """ 
     86        Return SBox object for self. 
     87        """ 
     88        return self._sbox 
     89 
     90    def polynomial_system(self, P=None, K=None): 
     91        """ 
     92        Return a tuple F,s for plaintext P and key K where F is an 
     93        MPolynomialSystem and s a dictionary which maps key variables 
     94        to their solutions. 
     95         
     96        INPUT: 
     97            P -- plaintext (vector, list) 
     98            K -- key (vector, list) 
     99        """ 
     100        raise NotImplementedError 
     101 
     102    def random_element(self): 
     103        """ 
     104        Return random element. Usually this is a list of elements in 
     105        the base field of length 'blocksize'. 
     106        """ 
     107        raise NotImplementedError 
     108 
  • (a) /dev/null vs. (b) b/sage/crypto/mq/sr.py

    diff -r fb0db5af8af0 -r 793d40d163d7 sage/crypto/mq/sr.py
    a b  
     1r""" 
     2Small Scale Variants of the AES (SR) Polynomial System Generator. 
     3 
     4We support polynomial system generation over $GF(2)$ and 
     5$GF(2^e)$. Also, we support both the specification of SR as given in 
     6the paper and we support a variant of SR* which is AES. 
     7 
     8AUTHORS: 
     9    -- Martin Albrecht <malb@informatik.uni-bremen.de> (2007-09) initial version 
     10 
     11EXAMPLES: 
     12    sage: sr = mq.SR(1,1,1,4) 
     13 
     14    $n$ is the number of rounds, $r$ the number of rows in the state 
     15    array, $c$ the number of columns in the state array. $e$ the 
     16    degree of the underlying field. 
     17 
     18    sage: sr.n, sr.r, sr.c, sr.e 
     19    (1, 1, 1, 4) 
     20 
     21    By default variables are ordered reverse to as they appear., e.g.: 
     22     
     23    sage: sr.R 
     24    Polynomial Ring in k100, k101, k102, k103, x100, x101, x102, x103, w100, w101, w102, w103, s000, s001, s002, s003, k000, k001, k002, k003 over Finite Field in a of size 2^4 
     25 
     26    For SR(1,1,1,4) the ShiftRows matrix isn't that interresting: 
     27 
     28    sage: sr.ShiftRows 
     29    [1 0 0 0] 
     30    [0 1 0 0] 
     31    [0 0 1 0] 
     32    [0 0 0 1] 
     33 
     34    Also, the MixColumns matrix is the identity matrix: 
     35 
     36    sage: sr.MixColumns 
     37    [1 0 0 0] 
     38    [0 1 0 0] 
     39    [0 0 1 0] 
     40    [0 0 0 1] 
     41 
     42    Lin, however is not the identity matrix. 
     43 
     44    sage: sr.Lin 
     45    [          a^2 + 1                 1         a^3 + a^2           a^2 + 1] 
     46    [                a                 a                 1 a^3 + a^2 + a + 1] 
     47    [          a^3 + a               a^2               a^2                 1] 
     48    [                1               a^3             a + 1             a + 1] 
     49 
     50 
     51    M and Mstar are identical for SR(1,1,1,4) 
     52 
     53    sage: sr.M 
     54    [          a^2 + 1                 1         a^3 + a^2           a^2 + 1] 
     55    [                a                 a                 1 a^3 + a^2 + a + 1] 
     56    [          a^3 + a               a^2               a^2                 1] 
     57    [                1               a^3             a + 1             a + 1] 
     58 
     59    sage: sr.Mstar 
     60    [          a^2 + 1                 1         a^3 + a^2           a^2 + 1] 
     61    [                a                 a                 1 a^3 + a^2 + a + 1] 
     62    [          a^3 + a               a^2               a^2                 1] 
     63    [                1               a^3             a + 1             a + 1] 
     64 
     65 
     66REFERENCES: 
     67   C. Cid , S. Murphy, and M.J.B. Robshaw; Small Scale Variants of the 
     68   AES; in Proceedings of Fast Software Encryption 2005, LNCS 3557; 
     69   Springer 2005; available at http://www.isg.rhul.ac.uk/~sean/smallAES-fse05.pdf 
     70 
     71   C. Cid , S. Murphy, and M.J.B. Robshaw; Algebraic Aspects of the 
     72   Advanced Encryption Standard; Springer 2006;   
     73""" 
     74 
     75from sage.rings.finite_field import GF 
     76from sage.rings.integer_ring import ZZ 
     77from sage.rings.polynomial.polynomial_ring import PolynomialRing 
     78 
     79from sage.matrix.matrix import is_Matrix 
     80from sage.matrix.constructor import Matrix, random_matrix 
     81from sage.matrix.matrix_space import MatrixSpace 
     82 
     83from sage.misc.misc import get_verbose, set_verbose 
     84from sage.misc.flatten import flatten 
     85 
     86from sage.modules.vector_modn_dense import Vector_modn_dense 
     87 
     88from mpolynomialsystem import MPolynomialSystem, MPolynomialRoundSystem 
     89from mpolynomialsystemgenerator import MPolynomialSystemGenerator 
     90 
     91def SR(n=1,r=1,c=1,e=4, star=False, **kwargs): 
     92    """ 
     93    Return a small scale variant of the AES polynomial system 
     94    constructor subject to the following conditions: 
     95 
     96    INPUT: 
     97        n -- the number of rounds (default: 1) 
     98        r -- the number of rows in the state array (default: 1) 
     99        c -- the number of columns in the state array (default: 1) 
     100        e -- the exponent of the finite extension field (default: 4) 
     101        star -- determines if SR* or SR should be constructed (default: False) 
     102        aes_mode -- as the SR key schedule specification differs slightly from the AES 
     103                    key schedule this parameter controls which schedule to use (default: True) 
     104        gf2 -- generate polynomial systems over GF(2) rather than over GF(2^n) (default: False) 
     105        order -- a string to specify the term ordering of the variables 
     106        postfix -- a string which is appended after the variable name (default: '') 
     107        allow_zero_inversions -- a boolean to controll whether zero inversions raise 
     108                                 an exception (default: False) 
     109 
     110    ADDITIONAL INPUT FOR GF(2): 
     111        correct_only -- only include correct inversion polynomials (default: False) 
     112        biaffine_only -- only include bilinear and biaffine inversion polynomials (default: True) 
     113    """ 
     114    if not kwargs.get("gf2",False): 
     115        return SR_gf2n(n,r,c,e,star,**kwargs) 
     116    else: 
     117        return SR_gf2(n,r,c,e,star,**kwargs) 
     118 
     119class SR_generic(MPolynomialSystemGenerator): 
     120    """ 
     121    Small Scale Variants of the AES. 
     122 
     123    EXAMPLES: 
     124        sage: sr = mq.SR(1,1,1,4) 
     125        sage: ShiftRows = sr.shift_rows_matrix() 
     126        sage: MixColumns = sr.mix_columns_matrix() 
     127        sage: Lin = sr.lin_matrix() 
     128        sage: M = MixColumns * ShiftRows * Lin 
     129        sage: print sr.hex_str_matrix(M) 
     130         5 1 C 5 
     131         2 2 1 F 
     132         A 4 4 1 
     133         1 8 3 3 
     134 
     135    TESTS: 
     136        sage: sr = mq.SR(1,2,1,4) 
     137        sage: ShiftRows = sr.shift_rows_matrix() 
     138        sage: MixColumns = sr.mix_columns_matrix() 
     139        sage: Lin = sr.lin_matrix() 
     140        sage: M = MixColumns * ShiftRows * Lin 
     141        sage: print sr.hex_str_matrix(M) 
     142         F 3 7 F A 2 B A 
     143         A A 5 6 8 8 4 9 
     144         7 8 8 2 D C C 3 
     145         4 6 C C 5 E F F 
     146         A 2 B A F 3 7 F 
     147         8 8 4 9 A A 5 6 
     148         D C C 3 7 8 8 2 
     149         5 E F F 4 6 C C 
     150 
     151        sage: sr = mq.SR(1,2,2,4) 
     152        sage: ShiftRows = sr.shift_rows_matrix() 
     153        sage: MixColumns = sr.mix_columns_matrix() 
     154        sage: Lin = sr.lin_matrix() 
     155        sage: M = MixColumns * ShiftRows * Lin 
     156        sage: print sr.hex_str_matrix(M) 
     157         F 3 7 F 0 0 0 0 0 0 0 0 A 2 B A 
     158         A A 5 6 0 0 0 0 0 0 0 0 8 8 4 9 
     159         7 8 8 2 0 0 0 0 0 0 0 0 D C C 3 
     160         4 6 C C 0 0 0 0 0 0 0 0 5 E F F 
     161         A 2 B A 0 0 0 0 0 0 0 0 F 3 7 F 
     162         8 8 4 9 0 0 0 0 0 0 0 0 A A 5 6 
     163         D C C 3 0 0 0 0 0 0 0 0 7 8 8 2 
     164         5 E F F 0 0 0 0 0 0 0 0 4 6 C C 
     165         0 0 0 0 A 2 B A F 3 7 F 0 0 0 0 
     166         0 0 0 0 8 8 4 9 A A 5 6 0 0 0 0 
     167         0 0 0 0 D C C 3 7 8 8 2 0 0 0 0 
     168         0 0 0 0 5 E F F 4 6 C C 0 0 0 0 
     169         0 0 0 0 F 3 7 F A 2 B A 0 0 0 0 
     170         0 0 0 0 A A 5 6 8 8 4 9 0 0 0 0 
     171         0 0 0 0 7 8 8 2 D C C 3 0 0 0 0 
     172         0 0 0 0 4 6 C C 5 E F F 0 0 0 0 
     173     
     174    """ 
     175    def __init__(self,n=1,r=1,c=1,e=4, star=False, **kwargs): 
     176        """ 
     177        See help for SR. 
     178        """ 
     179        if n-1 not in range(10): 
     180            raise TypeError, "n must be between 1 and 10 (inclusive)" 
     181        self._n = n 
     182 
     183        if r not in (1,2,4): 
     184            raise TypeError, "r must be in (1,2,4)" 
     185        self._r = r 
     186 
     187        if c not in (1,2,4): 
     188            raise TypeError, "c must be in (1,2,4)" 
     189        self._c = c 
     190         
     191        if e not in (4,8): 
     192            raise TypeError, "e must be either 4 or 8" 
     193        self._e = e 
     194 
     195        self._star = bool(star) 
     196 
     197        self._base = self.base_ring() 
     198 
     199        # to generate table, allow zero inversions 
     200        self._allow_zero_inversions = True 
     201        sub_byte_lookup = dict([(e,self.sub_byte(e)) for e in self._base]) 
     202        self._sub_byte_lookup = sub_byte_lookup 
     203 
     204        self._postfix = kwargs.get("postfix", "") 
     205        self._order = kwargs.get("order", "degrevlex") 
     206        self._allow_zero_inversions= bool(kwargs.get("allow_zero_inversions", False)) 
     207        self._aes_mode = kwargs.get("aes_mode",True) 
     208        self._gf2 = kwargs.get("gf2",False) 
     209 
     210    def __getattr__(self, attr): 
     211        if attr == "e": 
     212            return self._e 
     213        elif attr == "c": 
     214            return self._c 
     215        elif attr == "n": 
     216            return self._n 
     217        elif attr == "r": 
     218            return self._r 
     219 
     220        elif attr == "R": 
     221            self.R = self.ring() 
     222            return self.R 
     223        elif attr == "k": 
     224            self.k = self.base_ring() 
     225            return self.k 
     226 
     227        elif attr == "M": 
     228            self.M = self.MixColumns * self.ShiftRows * self.Lin 
     229            return self.M 
     230        elif attr == "Mstar": 
     231            self.Mstar = self.MixColumns * self.ShiftRows * self.Lin 
     232            return self.Mstar 
     233        elif attr == "ShiftRows": 
     234            self.ShiftRows = self.shift_rows_matrix() 
     235            return self.ShiftRows 
     236        elif attr == "MixColumns": 
     237            self.MixColumns = self.mix_columns_matrix() 
     238            return self.MixColumns 
     239        elif attr == "Lin": 
     240            self.Lin = self.lin_matrix() 
     241            return self.Lin 
     242         
     243        raise AttributeError, "%s has no attribute %s"%(type(self),attr) 
     244 
     245    def _repr_(self): 
     246        if self._star: 
     247            return "SR*(%d,%d,%d,%d)"%(self._n,self._r,self._c,self._e)  
     248        else: 
     249            return "SR(%d,%d,%d,%d)"%(self._n,self._r,self._c,self._e) 
     250         
     251    def base_ring(self): 
     252        """ 
     253        Return the base field of self as determined through self.e. 
     254 
     255        EXAMPLE: 
     256            sage: sr = mq.SR(10,2,2,4) 
     257            sage: sr.base_ring().polynomial() 
     258            a^4 + a + 1 
     259 
     260            The Rijndael polynomial: 
     261 
     262            sage: sr = mq.SR(10,4,4,8) 
     263            sage: sr.base_ring().polynomial() 
     264            a^8 + a^4 + a^3 + a + 1 
     265        """ 
     266        try: 
     267            return self._base 
     268        except AttributeError: 
     269            pass 
     270 
     271        if self._e == 4: 
     272            self._base = GF(2**4,'a',modulus=(1,1,0,0,1)) 
     273        elif self._e == 8: 
     274            self._base = GF(2**8,'a',modulus=(1,1,0,1,1,0,0,0,1)) 
     275 
     276        return self._base 
     277 
     278    def sub_bytes(self, d): 
     279        """ 
     280        Perform the non-linear transform on d. 
     281 
     282        INPUT: 
     283            d -- state array or something coercable to a state array 
     284        """ 
     285        d = self.state_array(d) 
     286        return Matrix(self.base_ring(), d.nrows(), d.ncols(), [self.sub_byte(b) for b in d.list()]) 
     287 
     288    def sub_byte(self, b): 
     289        """ 
     290        Perform SubByte on a single byte/halfbyte b. 
     291 
     292        A ZeroDivision exception is raised if an attempt is made to 
     293        perform an inversion on the zero element. This can be disabled 
     294        by passing allow_zero_inversion=True to the constructor. A zero inversion 
     295        will result in an inconsisten equation system. 
     296 
     297        INPUT: 
     298            b -- an element in self.base_ring() 
     299 
     300        EXAMPLE: 
     301 
     302           The S-Box table for GF(2^4): 
     303         
     304           sage: sr = mq.SR(1,1,1,4, allow_zero_inversions=True) 
     305           sage: for e in sr.base_ring(): 
     306           ...    print '% 20s % 20s'%(e, sr.sub_byte(e)) 
     307                           0              a^2 + a 
     308                           a              a^2 + 1 
     309                         a^2                    a 
     310                         a^3              a^3 + 1 
     311                       a + 1                  a^2 
     312                     a^2 + a          a^2 + a + 1 
     313                   a^3 + a^2                a + 1 
     314                 a^3 + a + 1            a^3 + a^2 
     315                     a^2 + 1        a^3 + a^2 + a 
     316                     a^3 + a    a^3 + a^2 + a + 1 
     317                 a^2 + a + 1              a^3 + a 
     318               a^3 + a^2 + a                    0 
     319           a^3 + a^2 + a + 1                  a^3 
     320               a^3 + a^2 + 1                    1 
     321                     a^3 + 1        a^3 + a^2 + 1 
     322                           1          a^3 + a + 1 
     323         
     324        """ 
     325        if not b: 
     326            if not self._allow_zero_inversions: 
     327                raise ZeroDivisionError,  "A zero inversion occurred during an encryption or key schedule." 
     328            else: 
     329                return self.sbox_constant() 
     330        try: 
     331            return self._sub_byte_lookup[b] 
     332        except AttributeError: 
     333            pass 
     334         
     335        e = self.e 
     336        k = self.k 
     337        a = k.gen() 
     338 
     339        # inversion 
     340        b = b ** ( 2**e - 2 ) 
     341 
     342        # GF(2) linear map 
     343        if e == 4: 
     344            if not hasattr(self,"_L"): 
     345                self._L = Matrix(GF(2),4,4,[[1, 1, 1, 0], 
     346                                            [0, 1, 1, 1], 
     347                                            [1, 0, 1, 1], 
     348                                            [1, 1, 0, 1]]) 
     349 
     350        elif e==8: 
     351            if not hasattr(self,"_L"): 
     352                self._L = Matrix(GF(2),8,8,[[1, 0, 0, 0, 1, 1, 1, 1], 
     353                                            [1, 1, 0, 0, 0, 1, 1, 1], 
     354                                            [1, 1, 1, 0, 0, 0, 1, 1], 
     355                                            [1, 1, 1, 1, 0, 0, 0, 1], 
     356                                            [1, 1, 1, 1, 1, 0, 0, 0], 
     357                                            [0, 1, 1, 1, 1, 1, 0, 0], 
     358                                            [0, 0, 1, 1, 1, 1, 1, 0], 
     359                                            [0, 0, 0, 1, 1, 1, 1, 1]]) 
     360 
     361        b = k(self._L * b.vector()) 
     362 
     363        # constant addition 
     364        if e == 4: 
     365            b = b + k.fetch_int(6) 
     366        elif e == 8: 
     367            b = b + k.fetch_int(99) 
     368 
     369        return b 
     370 
     371    def sbox_constant(self): 
     372        """ 
     373        Return the sbox constant which is added after $L(x^{-1})$ was 
     374        performed. That is 0x63 if e == 8 or 0x6 if e == 4. 
     375 
     376        EXAMPLE: 
     377            sage: sr = mq.SR(10,1,1,8) 
     378            sage: sr.sbox_constant() 
     379            a^6 + a^5 + a + 1 
     380        """ 
     381        k = self.k 
     382        if self.e == 4: 
     383            return k.fetch_int(6) 
     384        elif self.e == 8: 
     385            return k.fetch_int(99) 
     386        else: 
     387            raise TypeError, "sbox constant only defined for e in (4,8)" 
     388             
     389    def shift_rows(self, d): 
     390        """ 
     391        Perform the ShiftRows operation on d. 
     392 
     393        INPUT: 
     394            d -- state arrary or something coercable to an state array 
     395 
     396        EXAMPLES: 
     397            sage: sr = mq.SR(10,4,4,4) 
     398            sage: E = sr.state_array() + 1; E 
     399            [1 0 0 0] 
     400            [0 1 0 0] 
     401            [0 0 1 0] 
     402            [0 0 0 1] 
     403             
     404            sage: sr.shift_rows(E) 
     405            [1 0 0 0] 
     406            [1 0 0 0] 
     407            [1 0 0 0] 
     408            [1 0 0 0] 
     409        """ 
     410        d = self.state_array(d) 
     411        ret = [] 
     412        for i in range(d.nrows()): 
     413            ret += list(d.row(i)[i%d.ncols():]) + list(d.row(i)[:i%d.ncols()]) 
     414        return Matrix(self.base_ring(), self._r, self._c, ret) 
     415 
     416    def mix_columns(self, d): 
     417        """ 
     418        Perform the MixColumns operation on d. 
     419 
     420        INPUT: 
     421            d -- state arrary or something coercable to an state array 
     422 
     423        EXAMPLES: 
     424            sage: sr = mq.SR(10,4,4,4) 
     425            sage: E = sr.state_array() + 1; E 
     426            [1 0 0 0] 
     427            [0 1 0 0] 
     428            [0 0 1 0] 
     429            [0 0 0 1] 
     430 
     431            sage: sr.mix_columns(E) 
     432            [    a a + 1     1     1] 
     433            [    1     a a + 1     1] 
     434            [    1     1     a a + 1] 
     435            [a + 1     1     1     a] 
     436        """ 
     437        d = self.state_array(d) 
     438        k = self.base_ring() 
     439        a = k.gen() 
     440        r = self._r 
     441        if r == 1: 
     442            M = Matrix(self.base_ring(),1,1, [[1]]) 
     443        elif r == 2: 
     444            M = Matrix(self.base_ring(),2,2, [[a + 1, a], 
     445                                              [a, a + 1]]) 
     446 
     447        elif r == 4: 
     448            M = Matrix(self.base_ring(),4,4, [[a, a+1, 1, 1], 
     449                                              [1, a, a+1, 1], 
     450                                              [1, 1, a, a+1], 
     451                                              [a+1, 1, 1, a]]) 
     452        ret =[] 
     453        for column in d.columns(): 
     454            ret.append(M * column) 
     455        # AES uses the column major ordering 
     456        return Matrix(k, d.ncols(), d.nrows(), ret).transpose() 
     457 
     458 
     459    def add_round_key(self, d, key): 
     460        """ 
     461        Perform the AddRoundKey operation on d using key. 
     462 
     463        INPUT: 
     464            d -- state arrary or something coercable to an state array 
     465            key -- state arrary or something coercable to an state array 
     466 
     467        EXAMPLE: 
     468            sage: sr = mq.SR(10,4,4,4) 
     469            sage: D = sr.random_state_array() 
     470            sage: K = sr.random_state_array() 
     471            sage: sr.add_round_key(D,K) == K + D 
     472            True 
     473        """ 
     474        d = self.state_array(d) 
     475        key = self.state_array(key) 
     476 
     477        return d+key 
     478 
     479    def state_array(self, d=None): 
     480        """ 
     481        Convert the parameter to a state array. 
     482 
     483        INPUT: 
     484            d -- None, a matrix, a list, or a tuple 
     485 
     486        EXAMPLES: 
     487            sage: sr = mq.SR(2,2,2,4) 
     488            sage: k = sr.base_ring() 
     489            sage: e1 = [k.fetch_int(e) for e in range(2*2)]; e1 
     490            [0, 1, a, a + 1] 
     491            sage: e2 = sr.phi( Matrix(k,2*2,1,e1) ) 
     492            sage: sr.state_array(e1) # note the column major ordering 
     493            [    0     a] 
     494            [    1 a + 1]             
     495            sage: sr.state_array(e2) 
     496            [    0     a] 
     497            [    1 a + 1] 
     498 
     499            sage: sr.state_array() 
     500            [0 0] 
     501            [0 0] 
     502 
     503        """ 
     504        r = self.r 
     505        c = self.c 
     506        e = self.e 
     507        k = self.base_ring() 
     508         
     509        if d is None: 
     510            return Matrix(k, r, c) 
     511         
     512        if is_Matrix(d): 
     513            if d.nrows() == r*c*e: 
     514                return Matrix(k, c, r, self.antiphi(d)).transpose() 
     515            elif d.ncols() == c and d.nrows() == r and d.base_ring() == k: 
     516                return d 
     517         
     518        if isinstance(d, tuple([list,tuple])): 
     519            return Matrix(k, c, r, d).transpose() 
     520 
     521    def is_state_array(self, d): 
     522        """ 
     523        Return True if d is a state array, i.e. has the correct dimensions and base field. 
     524        """ 
     525        return is_Matrix(d) and \ 
     526               d.nrows() == self.r and \ 
     527               d.ncols() == self.c and \ 
     528               d.base_ring() == self.base_ring() 
     529 
     530    def random_state_array(self): 
     531        """ 
     532        Return a random element in MatrixSpace(self.base_ring(),self.r,self.c). 
     533        """ 
     534        return random_matrix(self.base_ring(),self._r,self._c) 
     535 
     536    def random_vector(self): 
     537        """ 
     538        Return a random vector as it might appear in the algebraic 
     539        expression of self. 
     540 
     541        NOTE: Phi was already applied to the result. 
     542        """ 
     543        return self.vector( self.random_state_array() ) 
     544 
     545    def random_element(self, type = "vector"): 
     546        """ 
     547        Return a random element for self. 
     548 
     549        INPUT: 
     550            type -- either 'vector' or 'state array' (default: 'vector') 
     551 
     552        EXAMPLE: 
     553            sage: sr = mq.SR() 
     554            sage: sr.random_element() # random 
     555            [      a] 
     556            [    a^2] 
     557            [  a + 1] 
     558            [a^2 + 1] 
     559            sage: sr.random_element('state_array') # random 
     560            [a^3 + a + 1] 
     561        """ 
     562        if type == "vector": 
     563            return self.random_vector() 
     564        elif type == "state_array": 
     565            return self.random_state_array() 
     566        else: 
     567            raise TypeError, "parameter type not understood" 
     568 
     569    def key_schedule(self, kj, i): 
     570        """ 
     571        Return $k_i$ for a given $i$ and $k_j$ with $j = i-1$. 
     572         
     573        TESTS: 
     574            sage: sr = mq.SR(10,4,4,8, star=True, allow_zero_inversions=True) 
     575            sage: ki = sr.state_array() 
     576            sage: for i in range(10): 
     577            ...  ki = sr.key_schedule(ki,i+1) 
     578            sage: print sr.hex_str_matrix(ki) 
     579            B4 3E 23 6F  
     580            EF 92 E9 8F  
     581            5B E2 51 18  
     582            CB 11 CF 8E 
     583        """ 
     584        if i < 0: 
     585            raise TypeError, "i must be >= i" 
     586 
     587        if i == 0: 
     588            return kj 
     589         
     590        r = self.r 
     591        c = self.c 
     592        e = self.e 
     593        F = self.base_ring() 
     594        a = F.gen() 
     595        SubByte = self.sub_byte 
     596 
     597        rc = Matrix(F,r,c, ([a**(i-1)] * c) + [F(0)]*((r-1)*c) ) 
     598        ki = Matrix(F,r,c) 
     599 
     600        if r == 1: 
     601            s0 = SubByte(kj[0,c-1]) 
     602             
     603            if c > 1: 
     604                for q in range(c): 
     605                    ki[0,q] = s0 + sum([kj[0,t] for t in range(q+1) ]) 
     606            else: 
     607                ki[0,0] = s0  
     608 
     609        elif r == 2: 
     610            s0 = SubByte(kj[1,c-1]) 
     611            s1 = SubByte(kj[0,c-1]) 
     612 
     613            if c > 1: 
     614                for q in range(c): 
     615                    ki[0, q] = s0 + sum([ kj[0,t] for t in range(q+1) ]) 
     616                    ki[1, q] = s1 + sum([ kj[1,t] for t in range(q+1) ]) 
     617            else: 
     618                ki[0,0] = s0 
     619                ki[1,0] = s1 
     620 
     621        elif r == 4: 
     622 
     623            if self._aes_mode: 
     624                s0 = SubByte(kj[1,c-1]) 
     625                s1 = SubByte(kj[2,c-1]) 
     626                s2 = SubByte(kj[3,c-1]) 
     627                s3 = SubByte(kj[0,c-1]) 
     628            else: 
     629                s0 = SubByte(kj[3,c-1]) 
     630                s1 = SubByte(kj[2,c-1]) 
     631                s2 = SubByte(kj[1,c-1]) 
     632                s3 = SubByte(kj[0,c-1]) 
     633 
     634            if c > 1: 
     635                for q in range(c): 
     636                    ki[0,q] = s0 + sum([ kj[0,t] for t in range(q+1) ]) 
     637                    ki[1,q] = s1 + sum([ kj[1,t] for t in range(q+1) ]) 
     638                    ki[2,q] = s2 + sum([ kj[2,t] for t in range(q+1) ]) 
     639                    ki[3,q] = s3 + sum([ kj[3,t] for t in range(q+1) ]) 
     640 
     641            else: 
     642                ki[0,0] = s0 
     643                ki[1,0] = s1 
     644                ki[2,0] = s2 
     645                ki[3,0] = s3 
     646        ki += rc 
     647 
     648        return ki 
     649 
     650    def __call__(self, P, K): 
     651        r""" 
     652        Encrypts the plaintext $P$ using the key $K$. 
     653 
     654        Both must be given as state arrays or coercable to state arrays. 
     655 
     656        INPUTS: 
     657            P -- plaintext as state array or something coercable to a state array 
     658            K -- key as state array or something coercable to a state array 
     659         
     660        TESTS: 
     661            The official AES test vectors: 
     662 
     663            sage: sr = mq.SR(10,4,4,8, star=True, allow_zero_inversions=True) 
     664            sage: k = sr.base_ring() 
     665            sage: plaintext = sr.state_array([k.fetch_int(e) for e in range(16)]) 
     666            sage: key = sr.state_array([k.fetch_int(e) for e in range(16)]) 
     667            sage: print sr.hex_str_matrix( sr(plaintext, key) ) 
     668            0A 41 F1 C6  
     669            94 6E C3 53  
     670            0B F0 94 EA  
     671            B5 45 58 5A 
     672 
     673            Brian Gladman's development vectors (dev_vec.txt): 
     674 
     675            sage: sr = mq.SR(10,4,4,8,star=True,allow_zero_inversions=True, aes_mode=True) 
     676            sage: k = sr.base_ring() 
     677            sage: plain = '3243f6a8885a308d313198a2e0370734' 
     678            sage: key = '2b7e151628aed2a6abf7158809cf4f3c' 
     679            sage: set_verbose(2) 
     680            sage: cipher = sr(plain,key) 
     681            R[01].start   193DE3BEA0F4E22B9AC68D2AE9F84808 
     682            R[01].s_box   D42711AEE0BF98F1B8B45DE51E415230 
     683            R[01].s_row   D4BF5D30E0B452AEB84111F11E2798E5 
     684            R[01].m_col   046681E5E0CB199A48F8D37A2806264C 
     685            R[01].k_sch   A0FAFE1788542CB123A339392A6C7605 
     686            R[02].start   A49C7FF2689F352B6B5BEA43026A5049 
     687            R[02].s_box   49DED28945DB96F17F39871A7702533B 
     688            R[02].s_row   49DB873B453953897F02D2F177DE961A 
     689            R[02].m_col   584DCAF11B4B5AACDBE7CAA81B6BB0E5 
     690            R[02].k_sch   F2C295F27A96B9435935807A7359F67F 
     691            R[03].start   AA8F5F0361DDE3EF82D24AD26832469A 
     692            R[03].s_box   AC73CF7BEFC111DF13B5D6B545235AB8 
     693            R[03].s_row   ACC1D6B8EFB55A7B1323CFDF457311B5 
     694            R[03].m_col   75EC0993200B633353C0CF7CBB25D0DC 
     695            R[03].k_sch   3D80477D4716FE3E1E237E446D7A883B 
     696            R[04].start   486C4EEE671D9D0D4DE3B138D65F58E7 
     697            R[04].s_box   52502F2885A45ED7E311C807F6CF6A94 
     698            R[04].s_row   52A4C89485116A28E3CF2FD7F6505E07 
     699            R[04].m_col   0FD6DAA9603138BF6FC0106B5EB31301 
     700            R[04].k_sch   EF44A541A8525B7FB671253BDB0BAD00 
     701            R[05].start   E0927FE8C86363C0D9B1355085B8BE01 
     702            R[05].s_box   E14FD29BE8FBFBBA35C89653976CAE7C 
     703            R[05].s_row   E1FB967CE8C8AE9B356CD2BA974FFB53 
     704            R[05].m_col   25D1A9ADBD11D168B63A338E4C4CC0B0 
     705            R[05].k_sch   D4D1C6F87C839D87CAF2B8BC11F915BC 
     706            R[06].start   F1006F55C1924CEF7CC88B325DB5D50C 
     707            R[06].s_box   A163A8FC784F29DF10E83D234CD503FE 
     708            R[06].s_row   A14F3DFE78E803FC10D5A8DF4C632923 
     709            R[06].m_col   4B868D6D2C4A8980339DF4E837D218D8 
     710            R[06].k_sch   6D88A37A110B3EFDDBF98641CA0093FD 
     711            R[07].start   260E2E173D41B77DE86472A9FDD28B25 
     712            R[07].s_box   F7AB31F02783A9FF9B4340D354B53D3F 
     713            R[07].s_row   F783403F27433DF09BB531FF54ABA9D3 
     714            R[07].m_col   1415B5BF461615EC274656D7342AD843 
     715            R[07].k_sch   4E54F70E5F5FC9F384A64FB24EA6DC4F 
     716            R[08].start   5A4142B11949DC1FA3E019657A8C040C 
     717            R[08].s_box   BE832CC8D43B86C00AE1D44DDA64F2FE 
     718            R[08].s_row   BE3BD4FED4E1F2C80A642CC0DA83864D 
     719            R[08].m_col   00512FD1B1C889FF54766DCDFA1B99EA 
     720            R[08].k_sch   EAD27321B58DBAD2312BF5607F8D292F 
     721            R[09].start   EA835CF00445332D655D98AD8596B0C5 
     722            R[09].s_box   87EC4A8CF26EC3D84D4C46959790E7A6 
     723            R[09].s_row   876E46A6F24CE78C4D904AD897ECC395 
     724            R[09].m_col   473794ED40D4E4A5A3703AA64C9F42BC 
     725            R[09].k_sch   AC7766F319FADC2128D12941575C006E 
     726            R[10].s_box   E9098972CB31075F3D327D94AF2E2CB5 
     727            R[10].s_row   E9317DB5CB322C723D2E895FAF090794 
     728            R[10].k_sch   D014F9A8C9EE2589E13F0CC8B6630CA6 
     729            R[10].output  3925841D02DC09FBDC118597196A0B32 
     730            sage: set_verbose(0) 
     731        """ 
     732        r = self.r 
     733        c = self.c 
     734        n = self.n 
     735        e = self.e 
     736        F = self.base_ring() 
     737 
     738        _type = self.state_array 
     739 
     740        if isinstance(P,str): 
     741            P = self.state_array([F.fetch_int(ZZ(P[i:i+2],16)) for i in range(0,len(P),2)]) 
     742        if isinstance(K,str): 
     743            K = self.state_array([F.fetch_int(ZZ(K[i:i+2],16)) for i in range(0,len(K),2)]) 
     744         
     745        if self.is_state_array(P) and self.is_state_array(K): 
     746            _type = self.state_array 
     747        elif self.is_vector(P) and self.is_vector(K): 
     748            _type = self.vector 
     749        else: 
     750            raise TypeError, "plaintext or key parameter not understood" 
     751         
     752        P = self.state_array(P) 
     753        K = self.state_array(K) 
     754 
     755        AddRoundKey = self.add_round_key 
     756        SubBytes = self.sub_bytes 
     757        MixColumns = self.mix_columns 
     758        ShiftRows = self.shift_rows 
     759        KeyExpansion = self.key_schedule 
     760 
     761        P = AddRoundKey(P,K) 
     762 
     763        for r in range(self._n-1): 
     764            if get_verbose() >= 2: print "R[%02d].start   %s"%(r+1,self.hex_str_vector(P)) 
     765 
     766            P = SubBytes(P) 
     767            if get_verbose() >= 2: print "R[%02d].s_box   %s"%(r+1,self.hex_str_vector(P)) 
     768 
     769            P = ShiftRows(P) 
     770            if get_verbose() >= 2: print "R[%02d].s_row   %s"%(r+1,self.hex_str_vector(P)) 
     771 
     772            P = MixColumns(P) 
     773            if get_verbose() >= 2: print "R[%02d].m_col   %s"%(r+1,self.hex_str_vector(P)) 
     774 
     775            K = KeyExpansion(K,r+1) 
     776            if get_verbose() >= 2: print "R[%02d].k_sch   %s"%(r+1,self.hex_str_vector(K)) 
     777 
     778            P = AddRoundKey(P, K) 
     779 
     780        P = SubBytes(P) 
     781        if get_verbose() >= 2: print "R[%02d].s_box   %s"%(self.n,self.hex_str_vector(P)) 
     782 
     783        P = ShiftRows(P) 
     784        if get_verbose() >= 2: print "R[%02d].s_row   %s"%(self.n,self.hex_str_vector(P)) 
     785 
     786        if not self._star: 
     787            P = MixColumns(P) 
     788            if get_verbose() >= 2: print "R[%02d].m_col   %s"%(self.n,self.hex_str_vector(P)) 
     789 
     790        K = KeyExpansion(K,self._n) 
     791        if get_verbose() >= 2: print "R[%02d].k_sch   %s"%(self.n,self.hex_str_vector(K)) 
     792 
     793        P = AddRoundKey(P, K) 
     794        if get_verbose() >= 2: print "R[%02d].output  %s"%(self.n,self.hex_str_vector(P)) 
     795         
     796        return _type(P) 
     797 
     798    def hex_str(self, M, type="matrix"): 
     799        """ 
     800        Return a hex string for the provided AES state array/matrix. 
     801 
     802        INPUT: 
     803            M -- state array 
     804            type -- controls what to return, either 'matrix' or 'vector' 
     805                    (default: 'matrix') 
     806        """ 
     807        if type == "matrix": 
     808            return self._hex_str_matrix(M) 
     809        elif type == "vector": 
     810            return self._hex_str_vector(M) 
     811        else: 
     812            raise TypeError, "parameter type must either be 'matrix' or 'vector'" 
     813 
     814    def hex_str_matrix(self, M): 
     815        """ 
     816        Return a two-dimensional AES like representation of the matrix M. 
     817 
     818        That is, show the finite field elements as hex strings. 
     819 
     820        INPUT: 
     821            M -- an AES state array 
     822        """ 
     823        e = M.base_ring().degree() 
     824        st = [""] 
     825        for x in range(M.nrows()): 
     826            for y in range(M.ncols()): 
     827                if e == 8: 
     828                    st.append("%02X"%(int(str(M[x,y].int_repr())))) 
     829                else: 
     830                    st.append("%X"%(int(str(M[x,y].int_repr())))) 
     831            st.append("\n") 
     832        return " ".join(st) 
     833 
     834    def hex_str_vector(self, M): 
     835        """ 
     836        Return a one dimensional AES like representation of the matrix M. 
     837 
     838        That is, show the finite field elements as hex strings. 
     839 
     840        INPUT: 
     841            M -- an AES state array 
     842 
     843        """ 
     844        e = M.base_ring().degree() 
     845        st = [""] 
     846        for y in range(M.ncols()): 
     847            for x in range(M.nrows()): 
     848                if e == 8: 
     849                    st.append("%02X"%(int(str(M[x,y].int_repr())))) 
     850                else: 
     851                    st.append("%X"%(int(str(M[x,y].int_repr())))) 
     852            #st.append("\n") 
     853        return "".join(st) 
     854 
     855    def _insert_matrix_into_matrix(self, dst, src, row, col): 
     856        """ 
     857        Insert matrix src into matrix dst starting at row and col. 
     858 
     859        INPUT: 
     860            dst -- a matrix 
     861            src -- a matrix 
     862            row -- offset row 
     863            col -- offset columns 
     864 
     865        EXAMPLE: 
     866            sage: sr = mq.SR(10,4,4,4) 
     867            sage: a = sr.k.gen() 
     868            sage: A = sr.state_array() + 1; A 
     869            [1 0 0 0] 
     870            [0 1 0 0] 
     871            [0 0 1 0] 
     872            [0 0 0 1] 
     873            sage: B = Matrix(sr.base_ring(),2,2,[0,a,a+1,a^2]); B 
     874            [    0     a] 
     875            [a + 1   a^2] 
     876            sage: sr._insert_matrix_into_matrix(A,B,1,1) 
     877            [    1     0     0     0] 
     878            [    0     0     a     0] 
     879            [    0 a + 1   a^2     0] 
     880            [    0     0     0     1] 
     881        """ 
     882        for i in range(src.nrows()): 
     883            for j in range(src.ncols()): 
     884                dst[row+i,col+j] = src[i,j] 
     885        return dst 
     886 
     887 
     888    def varformatstr(self, name, n=None, rc=None, e=None): 
     889        """ 
     890        Return a format string which is understood by print et al. 
     891 
     892        If a numberical value is omitted the default value of self is 
     893        used. The numerical values (n,rc,e) are used to determine the 
     894        width of the respective fields in the format string. 
     895 
     896        INPUT: 
     897            name -- name of the variable 
     898            n -- number of rounds (default: None) 
     899            rc -- number of rows * number of cols (default: None) 
     900            e -- exponent of base field (default: None) 
     901 
     902        EXAMPLE: 
     903            sage: sr = mq.SR(1,2,2,4) 
     904            sage: sr.varformatstr('x') 
     905            'x%01d%01d%01d' 
     906            sage: sr.varformatstr('x', n=1000) 
     907            'x%03d%03d%03d' 
     908        """ 
     909        if n is None: 
     910            n = self.n 
     911        if rc is None: 
     912            rc = self.r * self.c 
     913        if e is None: 
     914            e = self.e 
     915         
     916        l = str(max([  len(str(rc-1)), len(str(n-1)), len(str(e-1)) ] )) 
     917        if name != "k": 
     918            pf = self._postfix 
     919        else: 
     920            pf = ""     
     921        format_string = name + pf + "%0" + l + "d" + "%0" + l + "d" + "%0" + l + "d" 
     922        return format_string 
     923 
     924    def varstrs(self, name, round, rc = None, e = None): 
     925        """ 
     926        Return a list of strings representing variables in self. 
     927 
     928        INPUT: 
     929            name -- variable name 
     930            round -- number of round to create variable strings for 
     931            rc -- number of rounds * number of columns in the state array 
     932            e -- exponent of base field 
     933 
     934        EXAMPLE: 
     935            sr = mq.SR(10,1,2,4) 
     936            sr._varstrs('x',2) 
     937            ['x200', 'x201', 'x202', 'x203', 'x210', 'x211', 'x212', 'x213'] 
     938 
     939        """ 
     940        if rc is None: 
     941            rc = self.r * self.c 
     942 
     943        if e is None: 
     944            e = self.e 
     945 
     946        n = self._n 
     947 
     948        format_string = self.varformatstr(name,n,rc,e) 
     949         
     950        return [format_string%(round,rci,ei) for rci in range(rc) for ei in range(e)] 
     951 
     952    def vars(self, name, round, rc=None, e=None): 
     953        """ 
     954        Return a list ofvariables in self. 
     955 
     956        INPUT: 
     957            name -- variable name 
     958            round -- number of round to create variable strings for 
     959            rc -- number of rounds * number of columns in the state array 
     960            e -- exponent of base field 
     961 
     962        EXAMPLE: 
     963            sr = mq.SR(10,1,2,4) 
     964            sr._vars('x',2) 
     965            [x200, x201, x202, x203, x210, x211, x212, x213] 
     966         
     967        """ 
     968        return [self.R(e) for e in self.varstrs(name,round,rc,e)] 
     969 
     970    def block_order(self): 
     971        """ 
     972        Return a block order for self where each round is a block. 
     973        """ 
     974        r = self.r 
     975        c = self.c 
     976        e = self.e 
     977        n = self.n 
     978        k = self.k 
     979 
     980        T = None  
     981        for _n in range(n): 
     982            T = TermOrder('degrevlex', r*e + 3*r*c*e ) + T 
     983 
     984        T += TermOrder('degrevlex',r*c*e); 
     985 
     986        return T 
     987 
     988    def ring(self, order=None): 
     989        """ 
     990        Construct a ring as a base ring for the polynomial system. 
     991 
     992        Variables are ordered in the reverse of their natural 
     993        ordering, i.e. the reverse of as they appear. 
     994        """ 
     995        r = self.r 
     996        c = self.c 
     997        e = self.e 
     998        n = self.n 
     999        if not self._gf2: 
     1000            k = self.base_ring() 
     1001        else: 
     1002            k = GF(2) 
     1003 
     1004        if order is not None: 
     1005            self._order = order 
     1006        if self._order == 'block': 
     1007            self._order = self.block_order() 
     1008 
     1009        names = [] 
     1010         
     1011        for _n in reversed(xrange(n)): 
     1012            names += self.varstrs("k",_n+1,r*c,e) 
     1013            names += self.varstrs("x",_n+1,r*c,e) 
     1014            names += self.varstrs("w",_n+1,r*c,e) 
     1015            names += self.varstrs("s",_n,r,e) 
     1016 
     1017        names +=  self.varstrs("k",0,r*c,e) 
     1018 
     1019 
     1020        return PolynomialRing(k, 2*n*r*c*e + (n+1)*r*c*e + n*r*e, names, order=self._order) 
     1021 
     1022    def round_polynomials(self, i, plaintext = None, ciphertext = None): 
     1023        r""" 
     1024        Return list of polynomials for a given round $i$. 
     1025 
     1026        If $i == 0$ a plaintext must be provided, if $i == n$ a 
     1027        ciphertext must be provided. 
     1028 
     1029        INPUT: 
     1030           i -- round number 
     1031           plaintext -- optional plaintext (mandatory in first round) 
     1032           ciphertext -- optional ciphertext (mandatory in last round) 
     1033 
     1034        OUTPUT: 
     1035            MPolynomialRoundSystem 
     1036 
     1037        EXAMPLE: 
     1038            sage: sr = mq.SR(1,1,1,4) 
     1039            sage: k = sr.base_ring() 
     1040            sage: p = [k.random_element() for _ in range(sr.r*sr.c)] 
     1041            sage: sr.round_polynomials(0,plaintext=p) # random 
     1042            [w100 + k000 + (a^2), w101 + k001 + (a + 1), w102 + k002 + (a^2 + 1), w103 + k003 + (a)] 
     1043        """ 
     1044        r = self._r 
     1045        c = self._c 
     1046        e = self._e 
     1047        n = self._n 
     1048        R = self.R 
     1049 
     1050        M = self.M 
     1051 
     1052        _vars = self.vars 
     1053 
     1054        if i == 0: 
     1055            w1 = Matrix(R, r*c*e, 1, _vars("w",1,r*c,e)) 
     1056            k0 = Matrix(R, r*c*e, 1, _vars("k",0,r*c,e)) 
     1057            if isinstance(plaintext,(tuple,list)) and len(plaintext) == r*c: 
     1058                plaintext = Matrix(R, r*c*e, 1, self.phi(plaintext)) 
     1059            return MPolynomialRoundSystem(R, w1 + k0 + plaintext) 
     1060         
     1061        elif i>0 and i<=n: 
     1062            xj = Matrix(R, r*c*e, 1, _vars("x",i,r*c,e)) 
     1063            ki = Matrix(R, r*c*e, 1, _vars("k",i,r*c,e)) 
     1064            rcon = Matrix(R, r*c*e, 1, self.phi([self.sbox_constant()]*r*c)) 
     1065             
     1066            if i < n: 
     1067                wj = Matrix(R, r*c*e, 1, _vars("w",i+1,r*c,e)) 
     1068            if i == n: 
     1069                if isinstance(ciphertext,(tuple,list)) and len(ciphertext) == r*c: 
     1070                    ciphertext = Matrix(R, r*c*e, 1, self.phi(ciphertext)) 
     1071                wj = ciphertext 
     1072 
     1073            lin = (wj + ki + M * xj + rcon).list() 
     1074 
     1075 
     1076            wi = Matrix(R, r*c*e, 1, _vars("w",i,r*c,e)) 
     1077            xi = Matrix(R, r*c*e, 1, _vars("x",i,r*c,e)) 
     1078            sbox = [] 
     1079            sbox += self.inversion_polynomials(xi,wi,r*c*e) 
     1080            sbox += self.field_polynomials("x",i) 
     1081            sbox += self.field_polynomials("w",i) 
     1082            return MPolynomialRoundSystem(R, lin + sbox) 
     1083 
     1084    def key_schedule_polynomials(self, i): 
     1085        """ 
     1086        Return polynomials for the $i$-th round of the key schedule. 
     1087 
     1088        INPUT: 
     1089            i -- round (0 <= i <= n) 
     1090        """ 
     1091        R = self.R 
     1092        r = self.r 
     1093        e = self.e 
     1094        c = self.c 
     1095        k = self.k 
     1096        a = k.gen() 
     1097 
     1098        if i < 0: 
     1099            raise TypeError, "i must by >= 0" 
     1100         
     1101        if i == 0: 
     1102            return MPolynomialRoundSystem(R, self.field_polynomials("k",i, r*c)) 
     1103        else: 
     1104            L = self.lin_matrix(r) 
     1105            ki = Matrix(R, r*c*e, 1, self.vars("k", i  ,r*c,e)) 
     1106            kj = Matrix(R, r*c*e, 1, self.vars("k", i-1,r*c,e)) 
     1107            si = Matrix(R, r*e, 1, self.vars("s",i-1,r,e)) 
     1108 
     1109            rc = Matrix(R, r*e, 1, self.phi([a**(i-1)] + [k(0)]*(r-1)) ) 
     1110            d  = Matrix(R, r*e, 1, self.phi([self.sbox_constant()]*r) ) 
     1111 
     1112            sbox = [] 
     1113 
     1114            sbox += self.field_polynomials("k",i) 
     1115            sbox += self.field_polynomials("s",i-1, r) 
     1116 
     1117            if r == 1: 
     1118                sbox += self.inversion_polynomials(kj[(c - 1)*e:(c - 1)*e + e], si[0:e], e) 
     1119            if r == 2: 
     1120                sbox += self.inversion_polynomials( kj[(2*c -1)*e : (2*c -1)*e + e] , si[0:1*e], e ) 
     1121                sbox += self.inversion_polynomials( kj[(2*c -2)*e : (2*c -2)*e + e] , si[e:2*e], e ) 
     1122            if r == 4: 
     1123                if self._aes_mode: 
     1124                    sbox += self.inversion_polynomials( kj[(4*c-3)*e  : (4*c-3)*e + e] , si[0*e : 1*e] , e ) 
     1125                    sbox += self.inversion_polynomials( kj[(4*c-2)*e  : (4*c-2)*e + e] , si[1*e : 2*e] , e ) 
     1126                    sbox += self.inversion_polynomials( kj[(4*c-1)*e  : (4*c-1)*e + e] , si[2*e : 3*e] , e ) 
     1127                    sbox += self.inversion_polynomials( kj[(4*c-4)*e  : (4*c-4)*e + e] , si[3*e : 4*e] , e ) 
     1128                else: 
     1129                    sbox += self.inversion_polynomials( kj[(4*c-1)*e  : (4*c-1)*e + e] , si[0*e : 1*e] , e ) 
     1130                    sbox += self.inversion_polynomials( kj[(4*c-2)*e  : (4*c-2)*e + e] , si[1*e : 2*e] , e ) 
     1131                    sbox += self.inversion_polynomials( kj[(4*c-3)*e  : (4*c-3)*e + e] , si[2*e : 3*e] , e ) 
     1132                    sbox += self.inversion_polynomials( kj[(4*c-4)*e  : (4*c-4)*e + e] , si[3*e : 4*e] , e ) 
     1133 
     1134            si =  L * si + d + rc 
     1135            Sum = Matrix(R, r*e, 1) 
     1136            lin = [] 
     1137            if c>1: 
     1138                for q in range(c): 
     1139                    t = range(r*e*(q) , r*e*(q+1) ) 
     1140                    Sum += kj.matrix_from_rows(t) 
     1141                    lin += (ki.matrix_from_rows(t) + si + Sum).list() 
     1142 
     1143            else: 
     1144                lin += (ki + si).list() 
     1145            return MPolynomialRoundSystem(R, lin + sbox ) 
     1146 
     1147    def polynomial_system(self, P=None, K=None): 
     1148        """ 
     1149        Return a MPolynomialSystem for self for a given plaintext-key pair. 
     1150 
     1151        If none are provided a random pair will be generated. 
     1152 
     1153        INPUT: 
     1154            P -- vector, list, or tuple (default: None) 
     1155            K -- vector, list, or tuple (default: None) 
     1156        """ 
     1157        plaintext = P 
     1158        key = K 
     1159         
     1160        system = [] 
     1161        n = self._n 
     1162 
     1163        data = [] 
     1164 
     1165        for d in (plaintext, key): 
     1166            if d is None: 
     1167                data.append(self.random_element("vector")) 
     1168            elif isinstance(d, (tuple,list)): 
     1169                data.append( self.phi(self.state_array(d)) ) 
     1170            elif self.is_state_array(d): 
     1171                data.append( self.phi(d) ) 
     1172            elif self.is_vector(d): 
     1173                data.append( d ) 
     1174            else: 
     1175                raise TypeError, "type %s of %s not understood"%(type(d),d) 
     1176 
     1177        plaintext, key = data 
     1178 
     1179        ciphertext = self(plaintext, key) 
     1180 
     1181        for i in range(n+1): 
     1182            system.append( self.round_polynomials(i,plaintext, ciphertext) ) 
     1183            system.append( self.key_schedule_polynomials(i) ) 
     1184 
     1185        return MPolynomialSystem(self.R, system), dict(zip(self.vars("k",0),key.list())) 
     1186 
     1187 
     1188class SR_gf2n(SR_generic): 
     1189    r""" 
     1190    Small Scale Variants of the AES polynomial system constructor over $GF(2^n)$. 
     1191    """ 
     1192    def vector(self, d=None): 
     1193        """ 
     1194        Constructs a vector suitable for the algebraic representation of SR, i.e. BES. 
     1195 
     1196        INPUT: 
     1197            d -- values for vector, must be understood by self.phi (default:None) 
     1198        """ 
     1199        r = self.r 
     1200        c = self.c 
     1201        e = self.e 
     1202        k = self.base_ring() 
     1203 
     1204        if d is None: 
     1205            return Matrix(k, r*c*e, 1) 
     1206        elif d.ncols() == c and d.nrows() == r and d.base_ring() == k: 
     1207            return Matrix(k, r*c*e, 1, self.phi(d).transpose()) 
     1208 
     1209    def is_vector(self, d): 
     1210        """ 
     1211        Return True if d can be used as a vector for self. 
     1212        """ 
     1213        return is_Matrix(d) and \ 
     1214               d.nrows() == self.r*self.c*self.e and \ 
     1215               d.ncols() == 1 and \ 
     1216               d.base_ring() == self.base_ring() 
     1217 
     1218    def phi(self, l): 
     1219        r""" 
     1220        Projects state arrays to their algebraic representation. 
     1221        """ 
     1222        ret = [] 
     1223        if is_Matrix(l): 
     1224            for e in l.transpose().list(): 
     1225                ret += [e**(2**i) for i in range(self.e)] 
     1226        else: 
     1227            for e in l: 
     1228                ret += [e**(2**i) for i in range(self.e)] 
     1229        if isinstance(l,list): 
     1230            return ret 
     1231        elif isinstance(l,tuple): 
     1232            return tuple(ret) 
     1233        elif is_Matrix(l): 
     1234            return Matrix(l.base_ring(),l.ncols(), l.nrows()*self.e, ret).transpose() 
     1235        else: 
     1236            raise TypeError 
     1237 
     1238    def antiphi(self,l): 
     1239        """ 
     1240        Inverse of self.phi. 
     1241        """ 
     1242        if is_Matrix(l): 
     1243            ret = [e for e in l.transpose().list()[0:-1:self.e]] 
     1244        else: 
     1245            ret = [e for e in l[0:-1:self.e]] 
     1246         
     1247        if isinstance(l,list): 
     1248            return ret 
     1249        elif isinstance(l,tuple): 
     1250            return tuple(ret) 
     1251        elif is_Matrix(l): 
     1252            return Matrix(self.base_ring(),l.ncols(), l.nrows()/self.e, ret).transpose() 
     1253        else: 
     1254            raise TypeError 
     1255 
     1256    def shift_rows_matrix(self): 
     1257        """ 
     1258        Return the ShiftRows matrix. 
     1259 
     1260        EXAMPLE: 
     1261            sage: sr = mq.SR(1,2,2,4) 
     1262            sage: s = sr.random_state_array() 
     1263            sage: r1 = sr.shift_rows(s) 
     1264            sage: r2 = sr.state_array( sr.ShiftRows * sr.vector(s) ) 
     1265            sage: r1 == r2 
     1266            True 
     1267        """ 
     1268        e = self.e 
     1269        r = self.r 
     1270        c = self.c 
     1271        k = self.base_ring() 
     1272        bs = r*c*e 
     1273        shift_rows = Matrix(k,bs,bs) 
     1274        I = MatrixSpace(k,e,e)(1) 
     1275        for x in range(0, c): 
     1276            for y in range(0,r): 
     1277                _r = ((x*r)+y) * e 
     1278                _c = (((x*r)+((r+1)*y)) * e) % bs 
     1279                self._insert_matrix_into_matrix(shift_rows,I, _r, _c) 
     1280             
     1281        return shift_rows 
     1282 
     1283    def lin_matrix(self, length = None): 
     1284        """ 
     1285        Return the Lin matrix. 
     1286 
     1287        If no length is provided the standard state space size is 
     1288        used. The key schedule calls this method with an explicit 
     1289        length argument because only self.r S-Box applications are 
     1290        performed in the key schedule. 
     1291 
     1292        INPUT: 
     1293            length -- length of state space. (default: None) 
     1294        """ 
     1295        r = self.r 
     1296        c = self.c 
     1297        e = self.e 
     1298        k = self.k 
     1299 
     1300        if length is None: 
     1301            length = r*c 
     1302         
     1303        lin = Matrix(self.base_ring(), length*e, length*e) 
     1304        if e == 4: 
     1305            l = [ k.fetch_int(x) for x in  (5, 1, 12, 5) ] 
     1306            for k in range( 0, length ): 
     1307                for i in range(0,4): 
     1308                    for j in range(0,4): 
     1309                        lin[k*4+j,k*4+i]=  l[(i-j)%4] ** (2**j) 
     1310        elif e == 8:             
     1311            l = [ k.fetch_int(x) for x in  (5, 9, 249, 37, 244, 1, 181, 143) ] 
     1312            for k in range( 0, length ): 
     1313                for i in range(0,8): 
     1314                    for j in range(0,8): 
     1315                        lin[k*8+j,k*8+i]=  l[(i-j)%8] ** (2**j) 
     1316 
     1317        return lin 
     1318 
     1319    def mix_columns_matrix(self): 
     1320        """ 
     1321        Return the MixColumns matrix. 
     1322 
     1323        EXAMPLE: 
     1324            sage: sr = mq.SR(1,2,2,4) 
     1325            sage: s = sr.random_state_array() 
     1326            sage: r1 = sr.mix_columns(s) 
     1327            sage: r2 = sr.state_array(sr.MixColumns * sr.vector(s)) 
     1328            sage: r1 == r2 
     1329            True 
     1330        """ 
     1331 
     1332        def D(b): 
     1333            D = Matrix(self.base_ring(), self._e, self._e) 
     1334            for i in range(self._e): 
     1335                D[i,i] = b**(2**i)  
     1336            return D 
     1337 
     1338        r = self.r 
     1339        c = self.c 
     1340        e = self.e 
     1341        k = self.k 
     1342        a = k.gen() 
     1343 
     1344        M = Matrix(k,r*e,r*e) 
     1345         
     1346        if r == 1: 
     1347            self._insert_matrix_into_matrix(M,   D(1), 0, 0) 
     1348 
     1349        elif r == 2: 
     1350            self._insert_matrix_into_matrix(M, D(a+1), 0, 0) 
     1351            self._insert_matrix_into_matrix(M, D(a+1), e, e) 
     1352            self._insert_matrix_into_matrix(M,   D(a), e, 0) 
     1353            self._insert_matrix_into_matrix(M,   D(a), 0, e) 
     1354 
     1355        elif r == 4: 
     1356            self._insert_matrix_into_matrix(M,   D(a),   0,   0) 
     1357            self._insert_matrix_into_matrix(M,   D(a),   e,   e) 
     1358            self._insert_matrix_into_matrix(M,   D(a), 2*e, 2*e) 
     1359            self._insert_matrix_into_matrix(M,   D(a), 3*e, 3*e) 
     1360 
     1361            self._insert_matrix_into_matrix(M, D(a+1),   0,   e) 
     1362            self._insert_matrix_into_matrix(M, D(a+1),   e, 2*e) 
     1363            self._insert_matrix_into_matrix(M, D(a+1), 2*e, 3*e) 
     1364            self._insert_matrix_into_matrix(M, D(a+1), 3*e,   0) 
     1365 
     1366            self._insert_matrix_into_matrix(M,   D(1),   0, 2*e) 
     1367            self._insert_matrix_into_matrix(M,   D(1),   e, 3*e) 
     1368            self._insert_matrix_into_matrix(M,   D(1), 2*e,   0) 
     1369            self._insert_matrix_into_matrix(M,   D(1), 3*e, 1*e) 
     1370 
     1371            self._insert_matrix_into_matrix(M,   D(1),   0, 3*e) 
     1372            self._insert_matrix_into_matrix(M,   D(1),   e,   0) 
     1373            self._insert_matrix_into_matrix(M,   D(1), 2*e, 1*e) 
     1374            self._insert_matrix_into_matrix(M,   D(1), 3*e, 2*e) 
     1375 
     1376        mix_columns = Matrix(k,r*c*e,r*c*e) 
     1377 
     1378        for i in range(c): 
     1379            self._insert_matrix_into_matrix(mix_columns, M, r*e*i, r*e*i) 
     1380 
     1381        return mix_columns 
     1382 
     1383    def inversion_polynomials(self, xi, wi, length): 
     1384        """ 
     1385        Return polynomials to represent the inversion in the AES S-Box. 
     1386 
     1387        INPUT: 
     1388            xi -- output variables 
     1389            wi -- input variables 
     1390            length -- length of both lists 
     1391        """ 
     1392        return [xi[j,0]*wi[j,0] + 1 for j in range(length)] 
     1393 
     1394    def field_polynomials(self, name, i, l=None): 
     1395        """ 
     1396        Return list of conjugacy polynomials for a given round and name. 
     1397 
     1398        INPUT: 
     1399            name -- variable name 
     1400            i -- round number 
     1401            l -- r*c (default:None) 
     1402 
     1403        EXAMPLE: 
     1404            sage: sr = mq.SR(3,1,1,8) 
     1405            sage: sr.field_polynomials('x',2) 
     1406            [x200^2 + x201, 
     1407            x201^2 + x202, 
     1408            x202^2 + x203, 
     1409            x203^2 + x204, 
     1410            x204^2 + x205, 
     1411            x205^2 + x206, 
     1412            x206^2 + x207, 
     1413            x207^2 + x200] 
     1414        """ 
     1415        r = self._r 
     1416        c = self._c 
     1417        e = self._e 
     1418        n = self._n 
     1419 
     1420        if l is None: 
     1421            l = r*c 
     1422         
     1423        fms = self.varformatstr(name,n,l,e) 
     1424 
     1425        return [self.R( fms%(i,rci,ei) + "**2 + " + fms%(i, rci,(ei+1)%e) )  for rci in range(l)  for ei in range(e) ] 
     1426 
     1427     
     1428 
     1429class SR_gf2(SR_generic): 
     1430    r""" 
     1431    Small Scale Variants of the AES polynomial system constructor over $GF(2)$. 
     1432    """ 
     1433    def __init__(self,n=1,r=1,c=1,e=4, star=False, **kwargs): 
     1434        """ 
     1435        See help for SR. 
     1436 
     1437        """ 
     1438        SR_generic.__init__(self,n,r,c,e,star,**kwargs) 
     1439        self._correct_only = kwargs.get("correct_only",False) 
     1440        self._biaffine_only = kwargs.get("biaffine_only",True) 
     1441 
     1442    def vector(self, d=None): 
     1443        """ 
     1444        Constructs a vector suitable for the algebraic representation of SR. 
     1445 
     1446        INPUT: 
     1447            d -- values for vector(default:None) 
     1448        """ 
     1449        r = self.r 
     1450        c = self.c 
     1451        e = self.e 
     1452        k = GF(2) 
     1453 
     1454        if d is None: 
     1455            return Matrix(k, r*c*e, 1) 
     1456        elif is_Matrix(d) and d.ncols() == c and d.nrows() == r and d.base_ring() == self.k: 
     1457            l = flatten([self.phi(x) for x in d.transpose().list()], Vector_modn_dense) 
     1458            return Matrix(k, r*c*e, 1,l) 
     1459        elif isinstance(d,(list,tuple)): 
     1460            if len(d) == self.r*self.c: 
     1461                l = flatten([self.phi(x) for x in d], Vector_modn_dense) 
     1462                return Matrix(k, r*c*e, 1,l) 
     1463            elif len(d) == self.r*self.c*self.e: 
     1464                return Matrix(k, r*c*e, 1, d) 
     1465            else: 
     1466                raise TypeError 
     1467        else: 
     1468            raise TypeError 
     1469 
     1470    def is_vector(self, d): 
     1471        """ 
     1472        Return True if the given matrix satisfies the conditions for a vector 
     1473        as it appears in the algebraic expression of self. 
     1474 
     1475        INPUT: 
     1476            d -- matrix 
     1477        """ 
     1478        return is_Matrix(d) and \ 
     1479               d.nrows() == self.r*self.c*self.e and \ 
     1480               d.ncols() == 1 and \ 
     1481               d.base_ring() == GF(2) 
     1482 
     1483    def phi(self, l, diffusion_matrix=False): 
     1484        r""" 
     1485        Given a list/matrix of elements in $GF(2^n)$ return a matching 
     1486        list/matrix of elements in $GF(2)$. 
     1487 
     1488        INPUT: 
     1489            l -- element to perform phi on. 
     1490            diffusion_matrix -- if True the given matrix $l$ is transformed 
     1491                                to a matrix which performs the same operation 
     1492                                over GF(2) as $l$ over $GF(2^n)$ (default: False). 
     1493        """ 
     1494        ret = [] 
     1495        r,c,e = self.r,self.c,self.e 
     1496 
     1497        # handle diffusion layer matrices first 
     1498        if is_Matrix(l) and diffusion_matrix and \ 
     1499           l.nrows() == r*c and l.ncols() == r*c and \ 
     1500           l.base_ring() == self.k: 
     1501            B = Matrix(GF(2), r*c*e, r*c*e) 
     1502            for x in range(r*c): 
     1503                for y in range(r*c): 
     1504                    T = self._mul_matrix(l[x,y]) 
     1505                    self._insert_matrix_into_matrix(B,T,x*e, y*e) 
     1506            return B 
     1507 
     1508        # ground field elements 
     1509        if l in self.k: 
     1510            return list(reversed(l.vector())) 
     1511 
     1512        # remaining matrices 
     1513        if is_Matrix(l): 
     1514            for x in l.transpose().list(): 
     1515                ret += list(reversed(x.vector())) 
     1516        # or lists 
     1517        else: 
     1518            for x in l: 
     1519                ret += list(reversed(x.vector())) 
     1520                 
     1521        if isinstance(l,list): return ret 
     1522        elif isinstance(l,tuple): return tuple(ret) 
     1523        elif is_Matrix(l): return Matrix(GF(2), l.ncols(), l.nrows()*self.e, ret).transpose() 
     1524        else: raise TypeError 
     1525 
     1526    def antiphi(self,l): 
     1527        """ 
     1528        Inverse of self.phi. 
     1529        """ 
     1530        e = self.e 
     1531        V = self.k.vector_space() 
     1532 
     1533        if is_Matrix(l): 
     1534            l2 = l.transpose().list() 
     1535        else: 
     1536            l2 = l 
     1537 
     1538        ret = [] 
     1539        for i in range(0,len(l2),e): 
     1540            ret.append( self.k(V(list(reversed(l2[i:i+e])))) ) 
     1541 
     1542        if isinstance(l, list): 
     1543            return ret 
     1544        elif isinstance(l,tuple): 
     1545            return tuple(ret) 
     1546        elif is_Matrix(l): 
     1547            return Matrix(self.base_ring(), self.r *self.c, 1, ret) 
     1548        else: 
     1549            raise TypeError 
     1550 
     1551    def shift_rows_matrix(self): 
     1552        """ 
     1553        Return the ShiftRows matrix. 
     1554 
     1555        EXAMPLE: 
     1556            sage: sr = mq.SR(1,2,2,4,gf2=True) 
     1557            sage: s = sr.random_state_array() 
     1558            sage: r1 = sr.shift_rows(s) 
     1559            sage: r2 = sr.state_array( sr.ShiftRows * sr.vector(s) ) 
     1560            sage: r1 == r2 
     1561            True 
     1562        """ 
     1563        r = self.r 
     1564        c = self.c 
     1565        k = self.k 
     1566        bs = r*c 
     1567        shift_rows = Matrix(k,r*c,r*c) 
     1568        for x in range(0, c): 
     1569            for y in range(0,r): 
     1570                _r = ((x*r)+y) 
     1571                _c = ((x*r)+((r+1)*y)) % bs 
     1572                shift_rows[_r,_c] = 1 
     1573        return self.phi(shift_rows, diffusion_matrix=True) 
     1574 
     1575    def mix_columns_matrix(self): 
     1576        """ 
     1577        Return the MixColumns matrix. 
     1578 
     1579        EXAMPLE: 
     1580            sage: sr = mq.SR(1,2,2,4,gf2=True) 
     1581            sage: s = sr.random_state_array() 
     1582            sage: r1 = sr.mix_columns(s) 
     1583            sage: r2 = sr.state_array(sr.MixColumns * sr.vector(s)) 
     1584            sage: r1 == r2 
     1585            True 
     1586        """ 
     1587        r = self.r 
     1588        c = self.c 
     1589        k = self.k 
     1590        a = k.gen() 
     1591 
     1592         
     1593         
     1594        if r == 1: 
     1595            M = Matrix(k,r,r,1) 
     1596 
     1597        elif r == 2: 
     1598            M = Matrix(k,r,r,[a+1,a,a,a+1]) 
     1599 
     1600        elif r == 4: 
     1601            M = Matrix(k,r, [a,a+1,1,1,\ 
     1602                             1,a,a+1,1,\ 
     1603                             1,1,a,a+1,\ 
     1604                             a+1,1,1,a]) 
     1605 
     1606        mix_columns = Matrix(k,r*c,r*c) 
     1607 
     1608        for i in range(c): 
     1609            self._insert_matrix_into_matrix(mix_columns, M, r*i, r*i) 
     1610 
     1611        return self.phi(mix_columns, diffusion_matrix=True) 
     1612 
     1613    def lin_matrix(self, length=None): 
     1614        """ 
     1615        Return the Lin matrix. 
     1616 
     1617        If no length is provided the standard state space size is 
     1618        used. The key schedule calls this method with an explicit 
     1619        length argument because only self.r S-Box applications are 
     1620        performed in the key schedule. 
     1621 
     1622        INPUT: 
     1623            length -- length of state space. (default: None) 
     1624        """ 
     1625        r,c,e = self.r, self.c,self.e 
     1626 
     1627        if length is None: 
     1628            length = r*c 
     1629         
     1630        if e == 8: 
     1631            Z = Matrix(GF(2),8,8,[1,0,0,0,1,1,1,1,\ 
     1632                                  1,1,0,0,0,1,1,1,\ 
     1633                                  1,1,1,0,0,0,1,1,\ 
     1634                                  1,1,1,1,0,0,0,1,\ 
     1635                                  1,1,1,1,1,0,0,0,\ 
     1636                                  0,1,1,1,1,1,0,0,\ 
     1637                                  0,0,1,1,1,1,1,0,\ 
     1638                                  0,0,0,1,1,1,1,1]) 
     1639        else: 
     1640            Z = Matrix(GF(2),4,4,[1,1,1,0,\ 
     1641                                  0,1,1,1,\ 
     1642                                  1,0,1,1,\ 
     1643                                  1,1,0,1]) 
     1644 
     1645 
     1646        Z = Z.transpose() # account for endianess mismatch 
     1647 
     1648        lin = Matrix(GF(2),length*e,length*e) 
     1649         
     1650        for i in range(length): 
     1651            self._insert_matrix_into_matrix(lin,Z,i*e,i*e) 
     1652        return lin 
     1653 
     1654    def _mul_matrix(self, x): 
     1655        """ 
     1656        Given an element $x$ in self.base_ring() return a matrix which 
     1657        performs the same operation on a when interpreted over 
     1658        $GF(2)^e$ as $x$ over $GF(2^e)$. 
     1659 
     1660        INPUT: 
     1661            x -- an element in self.base_ring() 
     1662 
     1663        EXAMPLE: 
     1664            sage: sr = mq.SR(gf2=True) 
     1665            sage: a = sr.k.gen() 
     1666            sage: A = sr._mul_matrix(a^2+1) 
     1667            sage: sr.antiphi( A *  sr.vector([a+1]) )  
     1668            [a^3 + a^2 + a + 1] 
     1669 
     1670            sage: (a^2 + 1)*(a+1) 
     1671            a^3 + a^2 + a + 1 
     1672        """ 
     1673        a = self.k.gen() 
     1674        k = self.k 
     1675        e = self.e 
     1676        a = k.gen() 
     1677 
     1678        columns = [] 
     1679        for i in reversed(range(e)): 
     1680            columns.append( list(reversed((x * a**i).vector())) ) 
     1681        return Matrix(GF(2), e, e, columns).transpose() 
     1682 
     1683    def _square_matrix(self): 
     1684        """ 
     1685        Return a matrix of dimension self.e x self.e which performs 
     1686        the squaring operation over GF(2^n) on vectors of length e. 
     1687 
     1688        EXAMPLE: 
     1689            sage: sr = mq.SR(gf2=True) 
     1690            sage: a = sr.k.gen() 
     1691            sage: S = sr._square_matrix() 
     1692            sage: sr.antiphi( S *  sr.vector([a^3+1]) )  
     1693            [a^3 + a^2 + 1] 
     1694 
     1695            sage: (a^3 + 1)^2 
     1696            a^3 + a^2 + 1 
     1697         
     1698        """ 
     1699        a = self.k.gen() 
     1700        e = self.e 
     1701 
     1702        columns = [] 
     1703        for i in reversed(range(e)): 
     1704            columns.append( list(reversed(((a**i)**2).vector())) ) 
     1705        return Matrix(GF(2), e ,e, columns).transpose() 
     1706 
     1707    def inversion_polynomials_single_sbox(self, x= None, w=None, biaffine_only=None, correct_only=None): 
     1708        """ 
     1709        Generator for S-Box inversion polynomials of a single sbox. 
     1710 
     1711        INPUT: 
     1712            x -- output variables (default: None) 
     1713            w -- input variables  (default: None) 
     1714            biaffine_only -- only include biaffine polynomials (default: object default) 
     1715            correct_only -- only include correct polynomials (default: object default) 
     1716 
     1717        EXAMPLES: 
     1718            sage: sr = mq.SR(1,1,1,8,gf2=True) 
     1719            sage: len(sr.inversion_polynomials_single_sbox()) 
     1720            24 
     1721            sage: len(sr.inversion_polynomials_single_sbox(correct_only=True)) 
     1722            23 
     1723            sage: len(sr.inversion_polynomials_single_sbox(biaffine_only=False)) 
     1724            40 
     1725            sage: len(sr.inversion_polynomials_single_sbox(biaffine_only=False, correct_only=True)) 
     1726            39 
     1727        """ 
     1728        e = self.e 
     1729 
     1730        if biaffine_only is None: 
     1731            biaffine_only = self._biaffine_only 
     1732        if correct_only is None: 
     1733            correct_only = self._correct_only 
     1734         
     1735        if x is None and w is None: 
     1736            # make sure it prints like in the book. 
     1737            names = ["w%d"%i for i in reversed(range(e))]+ ["x%d"%i for i in reversed(range(e))] 
     1738            P = PolynomialRing(GF(2),e*2, names, order='lex') 
     1739            x = Matrix(P,e,1,P.gens()[e:]) 
     1740            w = Matrix(P,e,1,P.gens()[:e]) 
     1741        else: 
     1742            if isinstance(x,(tuple,list)): P = x[0].parent() 
     1743            elif is_Matrix(x): P = x.base_ring() 
     1744            else: raise TypeError, "x not understood" 
     1745 
     1746            if isinstance(x, (tuple,list)): 
     1747                x = Matrix(P,e,1, x) 
     1748            if isinstance(w, (tuple,list)): 
     1749                w = Matrix(P,e,1, w) 
     1750 
     1751        T = self._mul_matrix(self.k.gen()) 
     1752        o = Matrix(P,e,1,[0]*(e-1) + [1]) 
     1753 
     1754        columns = [] 
     1755        for i in reversed(range(e)): 
     1756            columns.append((T**i * w).list()) 
     1757        Cw = Matrix(P,e,e, columns).transpose() 
     1758 
     1759        columns = [] 
     1760        for i in reversed(range(e)): 
     1761            columns.append((T**i * x).list()) 
     1762        Cx = Matrix(P,e,e, columns).transpose() 
     1763 
     1764        S = self._square_matrix() 
     1765 
     1766        l = [] 
     1767        if correct_only: 
     1768            l.append( (Cw * x + o).list()[:-1] ) 
     1769        else: 
     1770            l.append( (Cw * x + o).list() ) 
     1771        l.append( (Cw * S *x  + x).list() ) 
     1772        l.append( (Cx * S *w  + w).list() ) 
     1773        if not biaffine_only: 
     1774            l.append( ((Cw * S**2 + Cx*S)*x).list() ) 
     1775            l.append( ((Cx * S**2 + Cw*S)*w).list() ) 
     1776 
     1777        return sum(l,[]) 
     1778     
     1779    def inversion_polynomials(self, xi, wi, length): 
     1780        """ 
     1781        Return polynomials to represent the inversion in the AES S-Box. 
     1782 
     1783        INPUT: 
     1784            xi -- output variables 
     1785            wi -- input variables 
     1786            length -- length of both lists 
     1787        """ 
     1788        if is_Matrix(xi): 
     1789            xi = xi.list() 
     1790        if is_Matrix(wi): 
     1791            wi = wi.list() 
     1792             
     1793        e = self.e 
     1794        l = [] 
     1795        for j in range(0,length, e): 
     1796            l += self.inversion_polynomials_single_sbox(xi[j:j+e], wi[j:j+e])  
     1797        return l 
     1798 
     1799 
     1800    def field_polynomials(self, name, i, l=None): 
     1801        """ 
     1802        Return list of field polynomials for a given round -- given by its number $i$ -- and name. 
     1803 
     1804        INPUT: 
     1805            name -- variable name 
     1806            i -- round number 
     1807            l -- length of variable list (default:None => r*c) 
     1808 
     1809        EXAMPLE: 
     1810            sage: sr = mq.SR(3,1,1,8) 
     1811            sage: sr.field_polynomials('x',2) 
     1812            [x200^2 + x201, 
     1813            x201^2 + x202, 
     1814            x202^2 + x203, 
     1815            x203^2 + x204, 
     1816            x204^2 + x205, 
     1817            x205^2 + x206, 
     1818            x206^2 + x207, 
     1819            x207^2 + x200] 
     1820        """ 
     1821        r = self._r 
     1822        c = self._c 
     1823        e = self._e 
     1824        n = self._n 
     1825 
     1826        if l is None: 
     1827            l = r*c 
     1828         
     1829        fms = self.varformatstr(name,n,l,e) 
     1830        return [self.R( fms%(i,rci,ei) + "**2 + " + fms%(i, rci,ei) )  for rci in range(l)  for ei in range(e) ] 
     1831 
     1832 
     1833def test_consistency(max_n=2, **kwargs): 
     1834    r""" 
     1835    Test all combinations of r,c,e and n in (1,2) for consistency of 
     1836    random encryptions and their polynomial systems. $GF(2)$ and $GF(2^e)$ 
     1837    systems are tested. This test takes a while. 
     1838 
     1839    INPUT: 
     1840        max_n -- maximal number of rounds to consider. 
     1841        kwargs -- are passed to the SR constructor 
     1842 
     1843    TESTS: 
     1844        sage: from sage.crypto.mq.sr import test_consistency 
     1845        sage: test_consistency(2) # long time 
     1846        True 
     1847    """ 
     1848    consistent = True 
     1849    for r in (1,2,4): 
     1850      for c in (1,2,4): 
     1851        for e in (4,8): 
     1852          for n in range(1,max_n+1): 
     1853            for gf2 in (True,False): 
     1854              zero_division = True 
     1855              while zero_division: 
     1856                sr = SR(n,r,c,e,gf2=gf2, **kwargs) 
     1857                try: 
     1858                  F, s = sr.polynomial_system() 
     1859                  F.subs(s) 
     1860                  consistent &= (F.groebner_basis('libsingular:slimgb')[0] != 1) 
     1861                  if not consistent: 
     1862                      print sr, " is not consistent" 
     1863                  zero_division = False 
     1864 
     1865                except ZeroDivisionError: 
     1866                    pass 
     1867    return consistent 
  • setup.py

    diff -r fb0db5af8af0 -r 793d40d163d7 setup.py
    a b  
    884884                     'sage.combinat', 
    885885                      
    886886                     'sage.crypto', 
     887 
     888                     'sage.crypto.mq', 
    887889                      
    888890                     'sage.databases', 
    889891