Ticket #8937: aes.sage

File aes.sage, 43.0 KB (added by amca01, 3 years ago)

Implementation of AES with different key sizes

Line 
1"""
2AES
3
4An implementation of the Advanced Encryption Standard (AES).  This only allows
5the encryption of a single 128-bit plaintext with either 128-bit, 192-bit or
6256-bit key.
7
8It thus provides a foundation for the user to build up standard NIST block
9modes (ECB, CBC etc), and for padding.  None of these are provided in this
10implementation.
11
12Note that the AES is already implemented in Sage, as part of the SR
13package:
14
15sage: from sage.crypto import mq
16sage: sr = mq.SR(10, 4, 4, 8, star=True, allow_zero_inversions=True, aes_mode=True)
17sage: k = sr.base_ring()
18sage: plain = '3243f6a8885a308d313198a2e0370734'
19sage: key = '2b7e151628aed2a6abf7158809cf4f3c'
20sage: set_verbose(2)
21sage: cipher = sr(plain, key)
22R[01].start   193DE3BEA0F4E22B9AC68D2AE9F84808
23
24[all intermediate values]
25
26R[10].output  3925841D02DC09FBDC118597196A0B32
27
28AUTHORS:
29
30- Alasdair McAndrew (2010-05): initial version
31"""
32
33###########################################################################
34# Copyright (c) 2010 Alasdair McAndrew <amca01@gmail.com>
35#
36# This program is free software; you can redistribute it and/or modify
37# it under the terms of the GNU General Public License as published by
38# the Free Software Foundation; either version 2 of the License, or
39# (at your option) any later version.
40#
41# This program is distributed in the hope that it will be useful,
42# but WITHOUT ANY WARRANTY; without even the implied warranty of
43# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
44# GNU General Public License for more details.
45#
46# http://www.gnu.org/licenses/
47###########################################################################
48
49from sage.structure.sage_object import SageObject
50
51class AES(SageObject):
52    """
53    This class implements the Advanced Encryption Standard (AES) as
54    described in NIST standard FIPS PUB 197 and available at
55    <http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf>
56
57    Input of plaintext is a 128 bit binary string, given as 32
58    hexadecimal characters.  The key can be any of 128-bit, 192-bit or
59    256-bits long, given as a hexadecimal string of 32, 48, or 64
60    characters respectively.  These are the only lengths of plaintext
61    and key allowed in the standard above.
62
63    EXAMPLES:
64   
65        sage: load aes.sage
66        sage: k='2b7e151628aed2a6abf7158809cf4f3c'
67        sage: A=AES(k)
68        sage: p='3243f6a8885a308d313198a2e0370734'
69        sage: c=A.encrypt(p)
70        sage: c
71          '3925841d02dc09fbdc118597196a0b32'
72        sage: A.decrypt(c)==p
73          True
74   
75    """
76
77    from sage.rings.finite_field import FiniteField as __GF
78   
79    __F=__GF(2^8,name='x',modulus=(1, 1, 0, 1, 1, 0, 0, 0, 1))
80
81    __bin2hex = {
82        # Given a 4-char bitstring, return the corresponding 1-char hexstring
83        "0000": "0", "0001": "1", "0010": "2", "0011": "3",
84        "0100": "4", "0101": "5", "0110": "6", "0111": "7",
85        "1000": "8", "1001": "9", "1010": "a", "1011": "b",
86        "1100": "c", "1101": "d", "1110": "e", "1111": "f",
87        }
88
89    # Make the reverse lookup table too
90    __hex2bin = {}
91    for (__b, __h) in __bin2hex.items():
92        __hex2bin[__h] = __b
93
94    __hex2dec ={
95        "0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,
96        "a":10,"A":10,"b":11,"B":11,"c":12,"C":12,"d":13,"D":13,"e":14,
97        "E":14,"f":15,"F":15,
98        }
99
100    def __init__(self,key):
101        # Sanity checking of arguments.
102        S1=set(x for x in key)
103        S2=set(['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','A','B','C','D','E','F'])
104        if not(S1.issubset(S2)):
105            raise ValueError("Invalid key - must be a hexadecimal string")
106        if not(len(key) in [32,48,64]):
107            raise ValueError("Invalid key length. Key must be exactly 32, 48 or 64 hexadecimal characters long.")
108        self.__key=key.lower()
109
110    def getkey(self):
111        """
112        Returns the value of the current key
113
114        INPUT:
115
116        None
117
118        OUTPUT:
119
120        A 32, 48 or 64 character hexadecimal string, representing the
121        current key.
122
123        EXAMPLES:
124
125            sage: load aes.sage
126            sage: k='2b7e151628aed2a6abf7158809cf4f3c'
127            sage: A=AES(k)
128            sage: A.getkey()
129              '2b7e151628aed2a6abf7158809cf4f3c'
130        """
131        return self.__key
132
133    def setkey(self,k):
134        """
135        Replaces the current key with a new value.
136
137        INPUT:
138
139        A 32, 48 or 64 character hexadecimal string, representing the
140        new key
141
142        OUTPUT:
143
144        None
145
146        EXAMPLES:
147       
148            sage: load aes.sage
149            sage: k='2b7e151628aed2a6abf7158809cf4f3c'
150            sage: A=AES(k)
151            sage: A.getkey()
152              '2b7e151628aed2a6abf7158809cf4f3c'
153            sage: A.setkey('000102030405060708090a0b0c0d0e0f')
154            sage: A.getkey()
155              '000102030405060708090a0b0c0d0e0f'
156
157        """
158        # First check values of k
159        S1=set(x for x in key)
160        S2=set(['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','A','B','C','D','E','F'])
161        if not(S1.issubset(S2)):
162            raise ValueError("Invalid key - must be a hexadecimal string")
163        if not(len(key) in [32,48,64]):
164            raise ValueError("Invalid key length. Key must be exactly 32, 48 or 64 hexadecimal characters long.")
165        self.__key=k.lower()
166
167       
168    def __bin2poly(self,z): # Converts a string of 8 bits to a polynomial
169        return sum(int(z[i])*x^(7-i) for i in range(8))
170
171    def __byte2poly(self,z): # Converts 2 hexadecimal characters to a polynomial
172        zb=self.__hex2bin[z[0]]+self.__hex2bin[z[1]]
173        return self.__bin2poly(zb)
174
175    def __poly2bin(self,p): # converts a polynomial to an 8-bit string
176        pv=list(vector(p))
177        pv.reverse()
178        pb=join([str(i) for i in pv],'')
179        return pb
180
181    def __poly2byte(self,p): # converts a polynomial to a 2-hex character btye
182        pb=self.__poly2bin(p)
183        return self.__bin2hex[pb[:4]]+self.__bin2hex[pb[4:]]
184
185    def __addroundkey(self,D,W,i):
186        # D is 4x4 array of 16 bytes, W is the keyexpansion, i is an integer
187        temp=[rows[4*i:4*i+4]  for rows in W]
188        return [self.__xor_word(x,y) for (x,y) in zip(D,temp)]
189
190    def mixcolumns(self,C): # C consists of a 4x4 array of bytes
191        """
192        Mixcolumns multiplies the current state (a 4x4 array of bytes)
193        by the matrix
194
195        [[x,x+1,1,1],
196         [1,x,x+1,1],
197         [1,1,x,x+1],
198         [x+1,1,1,x]]
199
200        where x is the generator of the finite field GF(2^8) generated
201        by the polynomial x^8+x^4+x^3+x+1.
202
203        INPUT:
204
205        A 4x4 array (represented as 4 lists of 4 elements each) of
206        bytes, each given as a string of two hexadecimal characters.
207
208        OUTPUT:
209
210        A 4x4 array (represented as 4 lists of 4 elements each) of
211        bytes, each given as a string of two hexadecimal characters.
212
213        EXAMPLES:
214       
215           sage: load aes.sage
216           sage: k='2b7e151628aed2a6abf7158809cf4f3c'
217           sage: A=AES(k)
218           sage: p=A.string2state('3243f6a8885a308d313198a2e0370734')
219           sage: p
220 
221           [['32', '88', '31', 'e0'],
222            ['43', '5a', '31', '37'],
223            ['f6', '30', '98', '07'],
224            ['a8', '8d', 'a2', '34']]
225
226           sage: A.mixcolumns(p)
227 
228           [['ff', '58', '0b', 'b1'],
229            ['1d', 'e1', '42', 'b3'],
230            ['65', '3e', 'd6', '85'],
231            ['a8', 'e8', 'a5', '63']]
232           
233        """
234        M=matrix(self.__F,[[x,x+1,1,1],[1,x,x+1,1],[1,1,x,x+1],[x+1,1,1,x]])
235        # P=matrix(map_threaded(self.__byte2poly,C))
236        P=matrix(map_threaded(self.__byte2poly,C))
237        Q=M*P
238        return map_threaded(self.__poly2byte,[list(row) for row in Q])
239
240    def inverse_mixcolumns(self,C): # C consists of a 4x4 array of
241        # bytes
242        """
243        Multiplies the current state (a 4x4 array of bytes) by the
244        matrix
245
246        [[x^3+x^2+x, x^3+x+1, x^3+x^2+1, x^3+1],
247         [x^3+1, x^3+x^2+x, x^3+x+1, x^3+x^2+1],
248         [x^3+x^2+1, x^3+1, x^3+x^2+x, x^3+x+1],
249         [x^3+x+1, x^3+x^2+1, x^3+1, x^3+x^2+x]]
250
251        where x is the generator of the finite field GF(2^8) generated
252        by the polynomial x^8+x^4+x^3+x+1.
253
254        INPUT:
255
256        A 4x4 array (represented as 4 lists of 4 elements each) of
257        bytes, each given as a string of two hexadecimal characters.
258
259        OUTPUT:
260
261        A 4x4 array (represented as 4 lists of 4 elements each) of
262        bytes, each given as a string of two hexadecimal characters.
263
264        EXAMPLES:
265
266            sage: load aes.sage
267            sage: k='2b7e151628aed2a6abf7158809cf4f3c'
268            sage: A=AES(k)
269            sage: p=A.string2state('3243f6a8885a308d313198a2e0370734')
270            sage: q=A.mixcolumns(p)
271            sage: mq=A.inverse_mixcolumns(q);mq
272 
273            [['32', '88', '31', 'e0'],
274             ['43', '5a', '31', '37'],
275             ['f6', '30', '98', '07'],
276             ['a8', '8d', 'a2', '34']]
277
278            sage: mq==p
279              True
280
281           
282        """
283        M=matrix(F,[[x^3+x^2+x, x^3+x+1, x^3+x^2+1, x^3+1],
284                    [x^3+1, x^3+x^2+x, x^3+x+1, x^3+x^2+1],
285                    [x^3+x^2+1, x^3+1, x^3+x^2+x, x^3+x+1],
286                    [x^3+x+1, x^3+x^2+1, x^3+1, x^3+x^2+x]])
287        P=matrix(map_threaded(self.__byte2poly,C))
288        Q=M*P
289        return map_threaded(self.__poly2byte,[list(row) for row in Q])
290
291    def shiftrows(self,B): # B consists of  a 4x4 array of bytes
292        """
293        Shiftrows takes a 4x4 array, and cyclicly shifts the first, second,
294        third and fourth rows to the left by 0, 1, 2 and 3 places
295        respectively.
296
297        INPUT:
298
299        A 4x4 array (represented as 4 lists of 4 elements each) of
300        bytes, each given as a string of two hexadecimal characters.
301
302        OUTPUT:
303
304        A 4x4 array (represented as 4 lists of 4 elements each) of
305        bytes, each given as a string of two hexadecimal characters.
306
307
308        EXAMPLES:
309       
310            sage: load aes.sage
311            sage: k='2b7e151628aed2a6abf7158809cf4f3c'
312            sage: A=AES(k)
313            sage: state=A.string2state('3243f6a8885a308d313198a2e0370734')
314            sage: state
315 
316            [['32', '88', '31', 'e0'],
317             ['43', '5a', '31', '37'],
318             ['f6', '30', '98', '07'],
319             ['a8', '8d', 'a2', '34']]
320            sage: A.shiftrows(state)
321 
322            [['32', '88', '31', 'e0'],
323             ['5a', '31', '37', '43'],
324             ['98', '07', 'f6', '30'],
325             ['34', 'a8', '8d', 'a2']]
326
327        """
328        temp=[B[0],B[1][1:]+B[1][:1],B[2][2:]+B[2][:2],B[3][3:]+B[3][:3]]
329        return temp
330
331    def inverse_shiftrows(self,B): # B consists of 16 bytes
332        """
333        Inverse_Shiftrows takes a 4x4 array, and cyclicly shifts the first, second,
334        third and fourth rows to the right by 0, 1, 2 and 3 places
335        respectively.
336
337        INPUT:
338
339        A 4x4 array (represented as 4 lists of 4 elements each) of
340        bytes, each given as a string of two hexadecimal characters.
341
342        OUTPUT:
343
344        A 4x4 array (represented as 4 lists of 4 elements each) of
345        bytes, each given as a string of two hexadecimal characters.
346
347        EXAMPLES:
348
349            sage: load aes.sage
350            sage: k='2b7e151628aed2a6abf7158809cf4f3c'
351            sage: A=AES(k)
352            sage: q=string2state('325a9834883107a83137f68de04330a2')
353            sage: q
354
355            [['32', '88', '31', 'e0'],
356             ['5a', '31', '37', '43'],
357             ['98', '07', 'f6', '30'],
358             ['34', 'a8', '8d', 'a2']]
359       
360            sage: A.inverse_shiftrows(q)
361
362            [['32', '88', '31', 'e0'],
363             ['43', '5a', '31', '37'],
364             ['f6', '30', '98', '07'],
365             ['a8', '8d', 'a2', '34']]
366       
367        """
368        temp=[B[0],B[1][3:]+B[1][:3],B[2][2:]+B[2][:2],B[3][1:]+B[3][:1]]
369        return temp
370
371    __sbox=[
372        '63','7c','77','7b','f2','6b','6f','c5','30','01','67','2b','fe','d7','ab','76',
373        'ca','82','c9','7d','fa','59','47','f0','ad','d4','a2','af','9c','a4','72','c0',
374        'b7','fd','93','26','36','3f','f7','cc','34','a5','e5','f1','71','d8','31','15',
375        '04','c7','23','c3','18','96','05','9a','07','12','80','e2','eb','27','b2','75',
376        '09','83','2c','1a','1b','6e','5a','a0','52','3b','d6','b3','29','e3','2f','84',
377        '53','d1','00','ed','20','fc','b1','5b','6a','cb','be','39','4a','4c','58','cf',
378        'd0','ef','aa','fb','43','4d','33','85','45','f9','02','7f','50','3c','9f','a8',
379        '51','a3','40','8f','92','9d','38','f5','bc','b6','da','21','10','ff','f3','d2',
380        'cd','0c','13','ec','5f','97','44','17','c4','a7','7e','3d','64','5d','19','73',
381        '60','81','4f','dc','22','2a','90','88','46','ee','b8','14','de','5e','0b','db',
382        'e0','32','3a','0a','49','06','24','5c','c2','d3','ac','62','91','95','e4','79',
383        'e7','c8','37','6d','8d','d5','4e','a9','6c','56','f4','ea','65','7a','ae','08',
384        'ba','78','25','2e','1c','a6','b4','c6','e8','dd','74','1f','4b','bd','8b','8a',
385        '70','3e','b5','66','48','03','f6','0e','61','35','57','b9','86','c1','1d','9e',
386        'e1','f8','98','11','69','d9','8e','94','9b','1e','87','e9','ce','55','28','df',
387        '8c','a1','89','0d','bf','e6','42','68','41','99','2d','0f','b0','54','bb','16'
388        ]
389
390    def sbox(self): # prints the sbox
391        """
392        Prints out the full sbox.
393
394        EXAMPLES:
395       
396            sage: load aes.sage
397            sage: k='2b7e151628aed2a6abf7158809cf4f3c'
398            sage: A=AES(k)
399            sage: A.sbox()
400            [63, 7c, 77, 7b, f2, 6b, 6f, c5, 30, 01, 67, 2b, fe, d7, ab, 76]
401
402            ...etc...
403       
404            [8c, a1, 89, 0d, bf, e6, 42, 68, 41, 99, 2d, 0f, b0, 54, bb, 16]
405
406        """
407        H=HexadecimalStrings()
408        for i in range(16):
409            print [H(self.__sbox[16*i+j]) for j in range(16)]
410
411    def sbox_check(self,b): # b is a byte made from 2 hex characters
412        p=self.__byte2poly(b)
413        q=self.__poly2byte(p^-1)
414        c=[0,1,1,0,0,0,1,1]
415        b=map(Integer, self.__hex2bin[q[1]]+self.__hex2bin[q[0]])
416        d=[]
417        for i in range(8):
418            d.append(b[i]^^b[(i+4)%8]^^b[(i+5)%8]^^b[(i+6)%8]^^b[(i+7)%8]^^c[i])
419        e=join([str(i) for i in d],'')
420        return self.__bin2hex[e[:4]]+self.__bin2hex[e[4:]]
421
422    def subbytes(self,A): # Replaces bytes with their indexed values
423        # from sbox
424        """
425        Subbytes applies the sbox to a list or array of bytes.
426
427        INPUT:
428
429        A 4x4 array (represented as 4 lists of 4 elements each) of
430        bytes, each given as a string of two hexadecimal characters.
431
432        OUTPUT:
433
434        A 4x4 array (represented as 4 lists of 4 elements each) of
435        bytes, each given as a string of two hexadecimal characters.
436
437        EXAMPLES:
438
439            sage: sage: load aes.sage
440            sage: k='2b7e151628aed2a6abf7158809cf4f3c'
441            sage: A=AES(k)
442            sage: state=A.string2state('3243f6a8885a308d313198a2e0370734')
443
444            sage: A.subbytes(state)
445 
446            [['23', 'c4', 'c7', 'e1'],
447             ['be', 'c7', '9a', '1a'],
448             ['46', 'c5', '42', '04'],
449             ['18', 'c2', '5d', '3a']]
450            sage: A.subbytes(['ef'])
451              ['df']
452
453        """
454        temp=map_threaded(lambda x:Integer('0x'+x),A)
455        return map_threaded(lambda i:self.__sbox[i],temp)
456
457    inverse_sbox=[
458        '52','09','6a','d5','30','36','a5','38','bf','40','a3','9e','81','f3','d7','fb',
459        '7c','e3','39','82','9b','2f','ff','87','34','8e','43','44','c4','de','e9','cb',
460        '54','7b','94','32','a6','c2','23','3d','ee','4c','95','0b','42','fa','c3','4e',
461        '08','2e','a1','66','28','d9','24','b2','76','5b','a2','49','6d','8b','d1','25',
462        '72','f8','f6','64','86','68','98','16','d4','a4','5c','cc','5d','65','b6','92',
463        '6c','70','48','50','fd','ed','b9','da','5e','15','46','57','a7','8d','9d','84',
464        '90','d8','ab','00','8c','bc','d3','0a','f7','e4','58','05','b8','b3','45','06',
465        'd0','2c','1e','8f','ca','3f','0f','02','c1','af','bd','03','01','13','8a','6b',
466        '3a','91','11','41','4f','67','dc','ea','97','f2','cf','ce','f0','b4','e6','73',
467        '96','ac','74','22','e7','ad','35','85','e2','f9','37','e8','1c','75','df','6e',
468        '47','f1','1a','71','1d','29','c5','89','6f','b7','62','0e','aa','18','be','1b',
469        'fc','56','3e','4b','c6','d2','79','20','9a','db','c0','fe','78','cd','5a','f4',
470        '1f','dd','a8','33','88','07','c7','31','b1','12','10','59','27','80','ec','5f',
471        '60','51','7f','a9','19','b5','4a','0d','2d','e5','7a','9f','93','c9','9c','ef',
472        'a0','e0','3b','4d','ae','2a','f5','b0','c8','eb','bb','3c','83','53','99','61',
473        '17','2b','04','7e','ba','77','d6','26','e1','69','14','63','55','21','0c','7d'
474        ]
475
476    def inverse_subbytes(self,A):
477        """
478        Inverse_Subbytes applies the inverse sbox to a list or array
479        of bytes.
480
481        INPUT:
482
483        A 4x4 array (represented as 4 lists of 4 elements each) of
484        bytes, each given as a string of two hexadecimal characters.
485
486        OUTPUT:
487
488        A 4x4 array (represented as 4 lists of 4 elements each) of
489        bytes, each given as a string of two hexadecimal characters.
490
491        EXAMPLES:
492
493            sage: load aes.sage
494            sage: k='2b7e151628aed2a6abf7158809cf4f3c'
495            sage: A=AES(k)
496            sage: state=A.string2state('325a9834883107a83137f68de04330a2')
497            sage: B=A.subbytes(state)
498            sage: C=A.inverse_subbytes(B)
499 
500            [['32', '88', '31', 'e0'],
501             ['5a', '31', '37', '43'],
502             ['98', '07', 'f6', '30'],
503             ['34', 'a8', '8d', 'a2']]
504
505            sage: C==state
506              True
507            sage: A.inverse_subbytes(['df'])
508              ['ef']
509
510
511        """
512        # Replaces bytes with their indexed values from inverse_sbox
513        temp=map_threaded(lambda x:Integer('0x'+x),A)
514        return map_threaded(lambda i:self.inverse_sbox[i],temp)
515
516    def __rotword(self,w): # w is a word of four bytes
517        return [w[1],w[2],w[3],w[0]]
518
519    def __xor_word(self,w1,w2): # exclusive-or of two words
520        return [hex(Integer('0x'+X)^^Integer('0x'+Y)).zfill(2) for (X,Y) in zip(w1,w2)]
521
522    def __rcon(self,i):
523        return [self.__poly2byte(x^(i-1)),'00','00','00']
524
525    def __keyexpansion(self):
526        k=self.__key
527        # k consists of 16, 24 or 32 bytes (32, 48, or 64 hexadecimal characters):
528        Nk=len(k)//8
529        Nr=Nk+6
530        Nb=4
531        W=[[k[8*i+2*j:8*i+2*j+2] for i in range(Nk)] for j in range(4)]
532        # puts k into an array to obtain first Nk subkeys
533        for i in range(Nk,Nb*(Nr+1)):
534            temp = [row[i-1] for row in W]
535            if i%Nk == 0:
536                temp = self.__xor_word(self.subbytes(self.__rotword(temp)),self.__rcon(i//Nk))
537            else:
538                if (Nk > 6 and i%Nk == 4):
539                    temp = self.subbytes(temp)
540            temp2=self.__xor_word([row[i-Nk] for row in W],temp)
541            for i in range(4):
542                W[i].append(temp2[i])
543        return W
544
545    def __inv_keyexpansion(self):
546        # This is the key expansion as required for the Equivalent
547        # Inverse Cipher.  The main difference is that all the
548        # subkeys, except for the first and last, are replaced by
549        # their outputs after an inverse_mixcolumn transformation.
550        k=self.__key
551        Nk=len(k)//8
552        Nr=Nk+6
553        Nb=4
554        DW=self.__keyexpansion()
555        for i in range(1,Nr):
556            temp=[row[Nb*i:Nb*(i+1)] for row in DW]
557            temp=self.inverse_mixcolumns(temp)
558            for j in range(4):
559                DW[j][Nb*i:Nb*(i+1)]=temp[j]
560        return DW
561         
562    def printkeys(self):
563        """
564        This prints out each key in the key expansion as a sequence of
565        strings of 16 bytes.  For ease of reading, the strings are
566        printed as groups of two hexdecimal characters separated by
567        spaces.
568       
569        If the key is longer, then there are more subkeys.  There are
570        examples of key schedules at
571        <http://www.samiam.org/key-schedule.html>
572
573        EXAMPLES:
574
575        INPUT:
576
577        None
578
579        OUTPUT:
580
581        A list of all the subkeys.
582
583        EXAMPLES:
584
585            sage: k='000102030405060708090a0b0c0d0e0f'
586            sage: A=AES(k)
587            sage: A.printkeys()
588            00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
589            d6 aa 74 fd d2 af 72 fa da a6 78 f1 d6 ab 76 fe
590            b6 92 cf 0b 64 3d bd f1 be 9b c5 00 68 30 b3 fe
591            b6 ff 74 4e d2 c2 c9 bf 6c 59 0c bf 04 69 bf 41
592            47 f7 f7 bc 95 35 3e 03 f9 6c 32 bc fd 05 8d fd
593            3c aa a3 e8 a9 9f 9d eb 50 f3 af 57 ad f6 22 aa
594            5e 39 0f 7d f7 a6 92 96 a7 55 3d c1 0a a3 1f 6b
595            14 f9 70 1a e3 5f e2 8c 44 0a df 4d 4e a9 c0 26
596            47 43 87 35 a4 1c 65 b9 e0 16 ba f4 ae bf 7a d2
597            54 99 32 d1 f0 85 57 68 10 93 ed 9c be 2c 97 4e
598            13 11 1d 7f e3 94 4a 17 f3 07 a7 8b 4d 2b 30 c5
599
600            sage: k='000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
601            sage: A=AES(k)
602            sage: A.printkeys()
603            00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
604            10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
605            a5 73 c2 9f a1 76 c4 98 a9 7f ce 93 a5 72 c0 9c
606            16 51 a8 cd 02 44 be da 1a 5d a4 c1 06 40 ba de
607            ae 87 df f0 0f f1 1b 68 a6 8e d5 fb 03 fc 15 67
608            6d e1 f1 48 6f a5 4f 92 75 f8 eb 53 73 b8 51 8d
609            c6 56 82 7f c9 a7 99 17 6f 29 4c ec 6c d5 59 8b
610            3d e2 3a 75 52 47 75 e7 27 bf 9e b4 54 07 cf 39
611            0b dc 90 5f c2 7b 09 48 ad 52 45 a4 c1 87 1c 2f
612            45 f5 a6 60 17 b2 d3 87 30 0d 4d 33 64 0a 82 0a
613            7c cf f7 1c be b4 fe 54 13 e6 bb f0 d2 61 a7 df
614            f0 1a fa fe e7 a8 29 79 d7 a5 64 4a b3 af e6 40
615            25 41 fe 71 9b f5 00 25 88 13 bb d5 5a 72 1c 0a
616            4e 5a 66 99 a9 f2 4f e0 7e 57 2b aa cd f8 cd ea
617            24 fc 79 cc bf 09 79 e9 37 1a c2 3c 6d 68 de 36
618
619       
620        """
621        k=self.__key
622        Nk=len(k)//8
623        W=self.__keyexpansion()
624        for i in range(Nk+7):
625            temp=[rows[4*i:4*i+4]  for rows in W]
626            print join([temp[i%4][i//4] for i in range(16)],' ')
627
628
629    def string2state(self,s):
630        """
631        This takes a string of 32 hexadecimal characters, and returns
632        a column-majored 4x4 array of bytes, each byte represented as two
633        consecutive characters from the original string.
634
635        INPUT:
636
637        A string of 32 hexadecimal characters.
638
639        OUTPUT:
640
641        A 4x4 array (represented as 4 lists of 4 elements each) of
642        bytes, each given as a string of two hexadecimal characters.
643
644        EXAMPLES:
645
646            sage: load aes.sage
647            sage: k='2b7e151628aed2a6abf7158809cf4f3c'
648            sage: A=AES(k)
649            sage: p='3243f6a8885a308d313198a2e0370734'
650            sage: A.string2state(p)
651
652            [['32', '88', '31', 'e0'],
653             ['43', '5a', '31', '37'],
654             ['f6', '30', '98', '07'],
655             ['a8', '8d', 'a2', '34']]
656
657 
658        """
659        return [[s[8*i+2*j:8*i+2*j+2] for i in range(4)] for j in range(4)]
660
661
662    def state2string(self,p):  # p consists of a 4x4 array of bytes
663        """
664        A state is a 4x4 array of bytes with each byte represented as
665        two hexadecimal characters.  This method joins all bytes
666        together in order of columns to make a single string.
667
668        INPUT:
669
670        A 4x4 array (represented as 4 lists of 4 elements each) of
671        bytes, each given as a string of two hexadecimal characters.
672
673        OUTPUT:
674
675        A string of 32 hexadecimal characters.
676
677        EXAMPLES:
678
679            sage: load aes.sage
680            sage: k='2b7e151628aed2a6abf7158809cf4f3c'
681            sage: A=AES(k)
682            sage: p='3243f6a8885a308d313198a2e0370734'
683            sage: state=A.string2state(p)
684            sage: A.state2string(state)
685              '3243f6a8885a308d313198a2e0370734'
686
687
688        """
689        temp=join([p[i%4][i//4] for i in range(16)],'')
690        return temp
691
692   
693    def encrypt(self,p):
694        """
695        This implements encryption with any of the allowed key sizes.
696
697        INPUT:
698
699        A string of 32 hexadecimal characters.
700
701        OUTPUT:
702
703        A string of 32 hexadecimal characters.
704
705        EXAMPLES:
706       
707            sage: load aes.sage
708            sage: key='000102030405060708090a0b0c0d0e0f'
709            sage: A=AES(key)
710            sage: p='00112233445566778899aabbccddeeff'
711            sage: c=A.encrypt(p)
712            sage: c
713              '69c4e0d86a7b0430d8cdb78070b4c55a'
714            sage: A.setkey('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f')
715            sage: c=A.encrypt(p);c
716              '8ea2b7ca516745bfeafc49904b496089'
717   
718        """
719        k=self.__key
720        Nr=6+len(k)//8 # Number of rounds
721        # First turn the hexadecimal string into a 4x4 array of bytes
722        state=A.string2state(p)
723        W=self.__keyexpansion()
724        state=self.__addroundkey(state,W,0)
725        for i in range(1,Nr):
726            state=self.subbytes(state)
727            state=self.shiftrows(state)
728            state=self.mixcolumns(state)
729            state=self.__addroundkey(state,W,i)
730        state=self.subbytes(state)
731        state=self.shiftrows(state)
732        state=self.__addroundkey(state,W,Nr)
733        return self.state2string(state)
734
735
736    def encrypt_with_print(self,p):
737        """
738        This implements encryption with any of the allowed key sizes,
739        and prints out all intermediate steps.
740
741        INPUT:
742
743        A string of 32 hexadecimal characters.
744
745        OUTPUT:
746
747        A string of 32 hexadecimal characters.
748
749        EXAMPLES:
750       
751            sage: load aes.sage
752            sage: key='000102030405060708090a0b0c0d0e0f'
753            sage: A=AES(key)
754            sage: p='00112233445566778899aabbccddeeff'
755            sage: c=A.encrypt_with_print(p)
756            round[ 0].input   00112233445566778899aabbccddeeff
757            round[ 0].k_sch   000102030405060708090a0b0c0d0e0f
758            round[ 1].start   00102030405060708090a0b0c0d0e0f0
759            round[ 1].s_box   63cab7040953d051cd60e0e7ba70e18c
760            round[ 1].s_row   6353e08c0960e104cd70b751bacad0e7
761            round[ 1].m_col   5f72641557f5bc92f7be3b291db9f91a
762            round[ 2].k_sch   d6aa74fdd2af72fadaa678f1d6ab76fe
763            round[ 2].start   89d810e8855ace682d1843d8cb128fe4
764            round[ 2].s_box   a761ca9b97be8b45d8ad1a611fc97369
765            round[ 2].s_row   a7be1a6997ad739bd8c9ca451f618b61
766            round[ 2].m_col   ff87968431d86a51645151fa773ad009
767            round[ 3].k_sch   b692cf0b643dbdf1be9bc5006830b3fe
768            round[ 3].start   4915598f55e5d7a0daca94fa1f0a63f7
769            round[ 3].s_box   3b59cb73fcd90ee05774222dc067fb68
770            round[ 3].s_row   3bd92268fc74fb735767cbe0c0590e2d
771            round[ 3].m_col   4c9c1e66f771f0762c3f868e534df256
772            round[ 4].k_sch   b6ff744ed2c2c9bf6c590cbf0469bf41
773            round[ 4].start   fa636a2825b339c940668a3157244d17
774            round[ 4].s_box   2dfb02343f6d12dd09337ec75b36e3f0
775            round[ 4].s_row   2d6d7ef03f33e334093602dd5bfb12c7
776            round[ 4].m_col   6385b79ffc538df997be478e7547d691
777            round[ 5].k_sch   47f7f7bc95353e03f96c32bcfd058dfd
778            round[ 5].start   247240236966b3fa6ed2753288425b6c
779            round[ 5].s_box   36400926f9336d2d9fb59d23c42c3950
780            round[ 5].s_row   36339d50f9b539269f2c092dc4406d23
781            round[ 5].m_col   f4bcd45432e554d075f1d6c51dd03b3c
782            round[ 6].k_sch   3caaa3e8a99f9deb50f3af57adf622aa
783            round[ 6].start   c81677bc9b7ac93b25027992b0261996
784            round[ 6].s_box   e847f56514dadde23f77b64fe7f7d490
785            round[ 6].s_row   e8dab6901477d4653ff7f5e2e747dd4f
786            round[ 6].m_col   9816ee7400f87f556b2c049c8e5ad036
787            round[ 7].k_sch   5e390f7df7a69296a7553dc10aa31f6b
788            round[ 7].start   c62fe109f75eedc3cc79395d84f9cf5d
789            round[ 7].s_box   b415f8016858552e4bb6124c5f998a4c
790            round[ 7].s_row   b458124c68b68a014b99f82e5f15554c
791            round[ 7].m_col   c57e1c159a9bd286f05f4be098c63439
792            round[ 8].k_sch   14f9701ae35fe28c440adf4d4ea9c026
793            round[ 8].start   d1876c0f79c4300ab45594add66ff41f
794            round[ 8].s_box   3e175076b61c04678dfc2295f6a8bfc0
795            round[ 8].s_row   3e1c22c0b6fcbf768da85067f6170495
796            round[ 8].m_col   baa03de7a1f9b56ed5512cba5f414d23
797            round[ 9].k_sch   47438735a41c65b9e016baf4aebf7ad2
798            round[ 9].start   fde3bad205e5d0d73547964ef1fe37f1
799            round[ 9].s_box   5411f4b56bd9700e96a0902fa1bb9aa1
800            round[ 9].s_row   54d990a16ba09ab596bbf40ea111702f
801            round[ 9].m_col   e9f74eec023020f61bf2ccf2353c21c7
802            round[10].k_sch   549932d1f08557681093ed9cbe2c974e
803            round[10].start   bd6e7c3df2b5779e0b61216e8b10b689
804            round[10].s_box   7a9f102789d5f50b2beffd9f3dca4ea7
805            round[10].s_row   7ad5fda789ef4e272bca100b3d9ff59f
806            round[10].k_sch   13111d7fe3944a17f307a78b4d2b30c5
807            round[10].output  69c4e0d86a7b0430d8cdb78070b4c55a
808              '69c4e0d86a7b0430d8cdb78070b4c55a'
809
810   
811        """
812
813        k=self.__key
814        Nr=6+len(k)//8 # Number of rounds
815        print "round[%2d].input   %s"%(0,str(p))
816        state=A.string2state(p)
817        print "round[%2d].k_sch   %s"%(0,str(k))
818        W=self.__keyexpansion()
819        state=self.__addroundkey(state,W,0)
820        print "round[%2d].start   %s"%(1,self.state2string(state))
821        for i in range(1,Nr):
822            state=self.subbytes(state)
823            print "round[%2d].s_box   %s"%(i,self.state2string(state))
824            state=self.shiftrows(state)
825            print "round[%2d].s_row   %s"%(i,self.state2string(state))
826            state=self.mixcolumns(state)
827            print "round[%2d].m_col   %s"%(i,self.state2string(state))
828            state=self.__addroundkey(state,W,i)
829            print "round[%2d].k_sch   %s"%(i+1,self.state2string([rows[4*i:4*i+4]  for rows in W]))
830            print "round[%2d].start   %s"%(i+1,self.state2string(state))
831        state=self.subbytes(state)
832        print "round[%2d].s_box   %s"%(Nr,self.state2string(state))
833        state=self.shiftrows(state)
834        print "round[%2d].s_row   %s"%(Nr,self.state2string(state))
835        state=self.__addroundkey(state,W,Nr)
836        print "round[%2d].k_sch   %s"%(Nr,self.state2string([rows[4*Nr:4*Nr+4]  for rows in W]))
837        print "round[%2d].output  %s"%(Nr,self.state2string(state))
838        return self.state2string(state)
839
840    def decrypt(self,c):
841        """
842        This implements decryption using the Inverse Cipher method
843        (Figure 11 in FIPS Pub 197)
844
845        INPUT:
846
847        A string of 32 hexadecimal characters.
848
849        OUTPUT:
850
851        A string of 32 hexadecimal characters.
852
853        EXAMPLES:
854       
855            sage: load aes.sage
856            sage: key='000102030405060708090a0b0c0d0e0f'
857            sage: A=AES(key)
858            sage: p='00112233445566778899aabbccddeeff'
859            sage: c=A.encrypt(p)
860            sage: c
861              '69c4e0d86a7b0430d8cdb78070b4c55a'
862            sage: A.decrypt(c)
863              '00112233445566778899aabbccddeeff'
864              sage: key='000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
865              sage: A=AES(key)
866              sage: c=A.encrypt(p);c
867                '8ea2b7ca516745bfeafc49904b496089'
868              sage: A.decrypt(c)
869                '00112233445566778899aabbccddeeff'
870
871        """
872        k=self.__key
873        Nr=6+len(k)//8 # Number of rounds
874        state=self.string2state(c)
875        W=self.__keyexpansion()
876        state=self.__addroundkey(state,W,Nr)
877        for i in range(Nr-1,0,-1):
878            state=self.inverse_shiftrows(state)
879            state=self.inverse_subbytes(state)
880            state=self.__addroundkey(state,W,i)
881            state=self.inverse_mixcolumns(state)
882        state=self.inverse_shiftrows(state)
883        state=self.inverse_subbytes(state)
884        state=self.__addroundkey(state,W,0)
885        return self.state2string(state)
886
887    def decrypt_with_print(self,c):
888        """
889        This implements decryption using the Inverse Cipher method
890        (Figure 11 in FIPS Pub 197) but prints out all intermediate
891        steps.
892
893        INPUT:
894
895        A string of 32 hexadecimal characters.
896
897        OUTPUT:
898
899        A string of 32 hexadecimal characters.
900
901        EXAMPLES:
902       
903            sage: load aes.sage
904            sage: key='000102030405060708090a0b0c0d0e0f'
905            sage: A=AES(key)
906            sage: p='00112233445566778899aabbccddeeff'
907            sage: c=A.encrypt(p)
908            sage: A.decrypt_with_print(c)
909            round[ 0].iinput   69c4e0d86a7b0430d8cdb78070b4c55a
910            round[ 0].ik_sch   13111d7fe3944a17f307a78b4d2b30c5
911            round[ 1].istart   7ad5fda789ef4e272bca100b3d9ff59f
912            round[ 1].is_row   7a9f102789d5f50b2beffd9f3dca4ea7
913            round[ 1].is_box   bd6e7c3df2b5779e0b61216e8b10b689
914            round[ 1].ik_sch   549932d1f08557681093ed9cbe2c974e
915            round[ 1].ik_add   e9f74eec023020f61bf2ccf2353c21c7
916            round[ 2].istart   54d990a16ba09ab596bbf40ea111702f
917            round[ 2].is_row   5411f4b56bd9700e96a0902fa1bb9aa1
918            round[ 2].is_box   fde3bad205e5d0d73547964ef1fe37f1
919            round[ 2].ik_sch   47438735a41c65b9e016baf4aebf7ad2
920            round[ 2].ik_add   baa03de7a1f9b56ed5512cba5f414d23
921            round[ 3].istart   3e1c22c0b6fcbf768da85067f6170495
922            round[ 3].is_row   3e175076b61c04678dfc2295f6a8bfc0
923            round[ 3].is_box   d1876c0f79c4300ab45594add66ff41f
924            round[ 3].ik_sch   14f9701ae35fe28c440adf4d4ea9c026
925            round[ 3].ik_add   c57e1c159a9bd286f05f4be098c63439
926            round[ 4].istart   b458124c68b68a014b99f82e5f15554c
927            round[ 4].is_row   b415f8016858552e4bb6124c5f998a4c
928            round[ 4].is_box   c62fe109f75eedc3cc79395d84f9cf5d
929            round[ 4].ik_sch   5e390f7df7a69296a7553dc10aa31f6b
930            round[ 4].ik_add   9816ee7400f87f556b2c049c8e5ad036
931            round[ 5].istart   e8dab6901477d4653ff7f5e2e747dd4f
932            round[ 5].is_row   e847f56514dadde23f77b64fe7f7d490
933            round[ 5].is_box   c81677bc9b7ac93b25027992b0261996
934            round[ 5].ik_sch   3caaa3e8a99f9deb50f3af57adf622aa
935            round[ 5].ik_add   f4bcd45432e554d075f1d6c51dd03b3c
936            round[ 6].istart   36339d50f9b539269f2c092dc4406d23
937            round[ 6].is_row   36400926f9336d2d9fb59d23c42c3950
938            round[ 6].is_box   247240236966b3fa6ed2753288425b6c
939            round[ 6].ik_sch   47f7f7bc95353e03f96c32bcfd058dfd
940            round[ 6].ik_add   6385b79ffc538df997be478e7547d691
941            round[ 7].istart   2d6d7ef03f33e334093602dd5bfb12c7
942            round[ 7].is_row   2dfb02343f6d12dd09337ec75b36e3f0
943            round[ 7].is_box   fa636a2825b339c940668a3157244d17
944            round[ 7].ik_sch   b6ff744ed2c2c9bf6c590cbf0469bf41
945            round[ 7].ik_add   4c9c1e66f771f0762c3f868e534df256
946            round[ 8].istart   3bd92268fc74fb735767cbe0c0590e2d
947            round[ 8].is_row   3b59cb73fcd90ee05774222dc067fb68
948            round[ 8].is_box   4915598f55e5d7a0daca94fa1f0a63f7
949            round[ 8].ik_sch   b692cf0b643dbdf1be9bc5006830b3fe
950            round[ 8].ik_add   ff87968431d86a51645151fa773ad009
951            round[ 9].istart   a7be1a6997ad739bd8c9ca451f618b61
952            round[ 9].is_row   a761ca9b97be8b45d8ad1a611fc97369
953            round[ 9].is_box   89d810e8855ace682d1843d8cb128fe4
954            round[ 9].ik_sch   d6aa74fdd2af72fadaa678f1d6ab76fe
955            round[ 9].ik_add   5f72641557f5bc92f7be3b291db9f91a
956            round[10].istart   6353e08c0960e104cd70b751bacad0e7
957            round[10].is_row   63cab7040953d051cd60e0e7ba70e18c
958            round[10].is_box   00102030405060708090a0b0c0d0e0f0
959            round[10].ik_sch   000102030405060708090a0b0c0d0e0f
960            round[10].ioutput  00112233445566778899aabbccddeeff
961              '00112233445566778899aabbccddeeff'
962
963
964        """
965        k=self.__key
966        Nr=6+len(k)//8 # Number of rounds
967        print "round[%2d].iinput   %s"%(0,str(c))
968        state=self.string2state(c)
969        W=self.__keyexpansion()
970        temp=[rows[Nr*4:Nr*4+4] for rows in W]
971        print "round[%2d].ik_sch   %s"%(0,self.state2string(temp))
972        W=self.__keyexpansion()
973        state=self.__addroundkey(state,W,Nr)
974        print "round[%2d].istart   %s"%(1,self.state2string(state))
975        for i in range(Nr-1,0,-1):
976            state=self.inverse_shiftrows(state)
977            print "round[%2d].is_row   %s"%(Nr-i,self.state2string(state))
978            state=self.inverse_subbytes(state)
979            print "round[%2d].is_box   %s"%(Nr-i,self.state2string(state))
980            state=self.__addroundkey(state,W,i)
981            temp=[rows[4*i:4*i+4]  for rows in W]
982            print "round[%2d].ik_sch   %s"%(Nr-i,self.state2string(temp))
983            print "round[%2d].ik_add   %s"%(Nr-i,self.state2string(state))
984            state=self.inverse_mixcolumns(state)
985            #print str(Nr-i).zfill(2),'im_col ',self.state2string(state)
986            #print str(Nr-i+1).zfill(2),'istart ',self.state2string(state)
987            print "round[%2d].istart   %s"%(Nr-i+1,self.state2string(state))
988           
989        state=self.inverse_shiftrows(state)
990        print "round[%2d].is_row   %s"%(Nr,self.state2string(state))
991        state=self.inverse_subbytes(state)
992        print "round[%2d].is_box   %s"%(Nr,self.state2string(state))
993        state=self.__addroundkey(state,W,0)
994        temp=[rows[0:4]  for rows in W]
995        print "round[%2d].ik_sch   %s"%(Nr,self.state2string(temp))
996        print "round[%2d].ioutput  %s"%(Nr,self.state2string(state))
997        return self.state2string(state)
998
999
1000    def eq_decrypt(self,c):
1001        """
1002        This implements decryption using the "Equivalent Inverse
1003        Cipher" as described in figure 15 of FIPS Pub 197.  The result
1004        is the same as for the Inverse Cipher, but the order of
1005        operations is different.  For that reason the intermediate
1006        results are automatically printed.
1007
1008       
1009        INPUT:
1010
1011        A string of 32 hexadecimal characters.
1012
1013        OUTPUT:
1014
1015        A string of 32 hexadecimal characters.
1016
1017        EXAMPLES:
1018       
1019            sage: load aes.sage
1020            sage: key='000102030405060708090a0b0c0d0e0f'
1021            sage: A=AES(key)
1022            sage: p='00112233445566778899aabbccddeeff'
1023            sage: c=A.encrypt(p)
1024            sage: A.eq_decrypt(c)
1025            round[ 0].iinput   69c4e0d86a7b0430d8cdb78070b4c55a
1026            round[ 0].ik_sch   13111d7fe3944a17f307a78b4d2b30c5
1027            round[ 1].istart   7ad5fda789ef4e272bca100b3d9ff59f
1028            round[ 1].is_box   bdb52189f261b63d0b107c9e8b6e776e
1029            round[ 1].is_row   bd6e7c3df2b5779e0b61216e8b10b689
1030            round[ 1].im_col   4773b91ff72f354361cb018ea1e6cf2c
1031            round[ 1].ik_sch   13aa29be9c8faff6f770f58000f7bf03
1032            round[ 2].istart   54d990a16ba09ab596bbf40ea111702f
1033            round[ 2].is_box   fde596f1054737d235febad7f1e3d04e
1034            round[ 2].is_row   fde3bad205e5d0d73547964ef1fe37f1
1035            round[ 2].im_col   2d7e86a339d9393ee6570a1101904e16
1036            round[ 2].ik_sch   1362a4638f2586486bff5a76f7874a83
1037            round[ 3].istart   3e1c22c0b6fcbf768da85067f6170495
1038            round[ 3].is_box   d1c4941f7955f40fb46f6c0ad68730ad
1039            round[ 3].is_row   d1876c0f79c4300ab45594add66ff41f
1040            round[ 3].im_col   39daee38f4f1a82aaf432410c36d45b9
1041            round[ 3].ik_sch   8d82fc749c47222be4dadc3e9c7810f5
1042            round[ 4].istart   b458124c68b68a014b99f82e5f15554c
1043            round[ 4].is_box   c65e395df779cf09ccf9e1c3842fed5d
1044            round[ 4].is_row   c62fe109f75eedc3cc79395d84f9cf5d
1045            round[ 4].im_col   9a39bf1d05b20a3a476a0bf79fe51184
1046            round[ 4].ik_sch   72e3098d11c5de5f789dfe1578a2cccb
1047            round[ 5].istart   e8dab6901477d4653ff7f5e2e747dd4f
1048            round[ 5].is_box   c87a79969b0219bc2526773bb016c992
1049            round[ 5].is_row   c81677bc9b7ac93b25027992b0261996
1050            round[ 5].im_col   18f78d779a93eef4f6742967c47f5ffd
1051            round[ 5].ik_sch   2ec410276326d7d26958204a003f32de
1052            round[ 6].istart   36339d50f9b539269f2c092dc4406d23
1053            round[ 6].is_box   2466756c69d25b236e4240fa8872b332
1054            round[ 6].is_row   247240236966b3fa6ed2753288425b6c
1055            round[ 6].im_col   85cf8bf472d124c10348f545329c0053
1056            round[ 6].ik_sch   a8a2f5044de2c7f50a7ef79869671294
1057            round[ 7].istart   2d6d7ef03f33e334093602dd5bfb12c7
1058            round[ 7].is_box   fab38a1725664d2840246ac957633931
1059            round[ 7].is_row   fa636a2825b339c940668a3157244d17
1060            round[ 7].im_col   fc1fc1f91934c98210fbfb8da340eb21
1061            round[ 7].ik_sch   c7c6e391e54032f1479c306d6319e50c
1062            round[ 8].istart   3bd92268fc74fb735767cbe0c0590e2d
1063            round[ 8].is_box   49e594f755ca638fda0a59a01f15d7fa
1064            round[ 8].is_row   4915598f55e5d7a0daca94fa1f0a63f7
1065            round[ 8].im_col   076518f0b52ba2fb7a15c8d93be45e00
1066            round[ 8].ik_sch   a0db02992286d160a2dc029c2485d561
1067            round[ 9].istart   a7be1a6997ad739bd8c9ca451f618b61
1068            round[ 9].is_box   895a43e485188fe82d121068cbd8ced8
1069            round[ 9].is_row   89d810e8855ace682d1843d8cb128fe4
1070            round[ 9].im_col   ef053f7c8b3d32fd4d2a64ad3c93071a
1071            round[ 9].ik_sch   8c56dff0825dd3f9805ad3fc8659d7fd
1072            round[10].istart   6353e08c0960e104cd70b751bacad0e7
1073            round[10].is_box   0050a0f04090e03080d02070c01060b0
1074            round[10].is_row   00102030405060708090a0b0c0d0e0f0
1075            round[10].ik_sch   13111d7fe3944a17f307a78b4d2b30c5
1076            round[10].ioutput  00112233445566778899aabbccddeeff
1077              '00112233445566778899aabbccddeeff'
1078
1079   
1080        """
1081       
1082        k=self.__key
1083        Nr=6+len(k)//8 # Number of rounds
1084        print "round[%2d].iinput   %s"%(0,str(c))
1085        # First turn the hexadecimal string into a 4x4 array of bytes
1086        state=self.string2state(c)
1087        DW=self.__inv_keyexpansion()
1088        temp=[rows[Nr*4:Nr*4+4] for rows in DW]
1089        print "round[%2d].ik_sch   %s"%(0,self.state2string(temp))
1090        state=self.__addroundkey(state,DW,Nr)
1091        print "round[%2d].istart   %s"%(1,self.state2string(state))
1092        for i in range(Nr-1,0,-1):
1093            state=self.inverse_subbytes(state)
1094            print "round[%2d].is_box   %s"%(Nr-i,self.state2string(state))
1095            state=self.inverse_shiftrows(state)
1096            print "round[%2d].is_row   %s"%(Nr-i,self.state2string(state))
1097            state=self.inverse_mixcolumns(state)
1098            print "round[%2d].im_col   %s"%(Nr-i,self.state2string(state))
1099            state=self.__addroundkey(state,DW,i)
1100            temp=[rows[4*i:4*i+4]  for rows in DW]
1101            print "round[%2d].ik_sch   %s"%(Nr-i,self.state2string(temp))
1102            print "round[%2d].istart   %s"%(Nr-i+1,self.state2string(state))
1103        state=self.inverse_subbytes(state)
1104        print "round[%2d].is_box   %s"%(Nr,self.state2string(state))
1105        state=self.inverse_shiftrows(state)
1106        print "round[%2d].is_row   %s"%(Nr,self.state2string(state))
1107        state=self.__addroundkey(state,DW,0)
1108        temp=[rows[4*Nr:4*Nr+4]  for rows in DW]
1109        print "round[%2d].ik_sch   %s"%(Nr,self.state2string(temp))
1110        print "round[%2d].ioutput  %s"%(Nr,self.state2string(state))
1111        return self.state2string(state)