2 A pure python (slow) implementation of rijndael with a decent interface
6 from rijndael import rijndael
10 r = rijndael(key, block_size = 16)
12 key must be a string of length 16, 24, or 32
13 blocksize must be 16, 24, or 32. Default is 16
17 ciphertext = r.encrypt(plaintext)
18 plaintext = r.decrypt(ciphertext)
20 If any strings are of the wrong length a ValueError is thrown
23 # ported from the Java reference code by Bram Cohen, bram@gawth.com, April 2001
24 # this code is public domain, unless someone makes
25 # an intellectual property claim against the reference
26 # code, in which case it can be made public domain by
27 # deleting all the comments and renaming all the variables
34 #-----------------------
35 #TREV - ADDED BECAUSE THERE'S WARNINGS ABOUT INT OVERFLOW BEHAVIOR CHANGING IN
40 if hasattr(exceptions
, "FutureWarning"):
42 warnings
.filterwarnings("ignore", category
=FutureWarning
, append
=1)
43 #-----------------------
47 shifts
= [[[0, 0], [1, 3], [2, 2], [3, 1]],
48 [[0, 0], [1, 5], [2, 4], [3, 3]],
49 [[0, 0], [1, 7], [3, 5], [4, 4]]]
51 # [keysize][block_size]
52 num_rounds
= {16: {16: 10, 24: 12, 32: 14}, 24: {16: 12, 24: 12, 32: 14}, 32: {16: 14, 24: 14, 32: 14}}
54 A
= [[1, 1, 1, 1, 1, 0, 0, 0],
55 [0, 1, 1, 1, 1, 1, 0, 0],
56 [0, 0, 1, 1, 1, 1, 1, 0],
57 [0, 0, 0, 1, 1, 1, 1, 1],
58 [1, 0, 0, 0, 1, 1, 1, 1],
59 [1, 1, 0, 0, 0, 1, 1, 1],
60 [1, 1, 1, 0, 0, 0, 1, 1],
61 [1, 1, 1, 1, 0, 0, 0, 1]]
63 # produce log and alog tables, needed for multiplying in the
64 # field GF(2^m) (generator = 3)
67 j
= (alog
[-1] << 1) ^ alog
[-1]
73 for i
in xrange(1, 255):
76 # multiply two elements of GF(2^m)
80 return alog
[(log
[a
& 0xFF] + log
[b
& 0xFF]) % 255]
82 # substitution box based on F^{-1}(x)
83 box
= [[0] * 8 for i
in xrange(256)]
85 for i
in xrange(2, 256):
86 j
= alog
[255 - log
[i
]]
88 box
[i
][t
] = (j
>> (7 - t
)) & 0x01
90 B
= [0, 1, 1, 0, 0, 0, 1, 1]
92 # affine transform: box[i] <- B + A*box[i]
93 cox
= [[0] * 8 for i
in xrange(256)]
98 cox
[i
][t
] ^
= A
[t
][j
] * box
[i
][j
]
100 # S-boxes and inverse S-boxes
103 for i
in xrange(256):
104 S
[i
] = cox
[i
][0] << 7
105 for t
in xrange(1, 8):
106 S
[i
] ^
= cox
[i
][t
] << (7-t
)
115 AA
= [[0] * 8 for i
in xrange(4)]
126 while AA
[t
][i
] == 0 and t
< 4:
128 assert t
!= 4, 'G matrix must be invertible'
130 AA
[i
][j
], AA
[t
][j
] = AA
[t
][j
], AA
[i
][j
]
134 AA
[i
][j
] = alog
[(255 + log
[AA
[i
][j
] & 0xFF] - log
[pivot
& 0xFF]) % 255]
137 for j
in xrange(i
+1, 8):
138 AA
[t
][j
] ^
= mul(AA
[i
][j
], AA
[t
][i
])
141 iG
= [[0] * 4 for i
in xrange(4)]
145 iG
[i
][j
] = AA
[i
][j
+ 4]
170 for t
in xrange(256):
172 T1
.append(mul4(s
, G
[0]))
173 T2
.append(mul4(s
, G
[1]))
174 T3
.append(mul4(s
, G
[2]))
175 T4
.append(mul4(s
, G
[3]))
178 T5
.append(mul4(s
, iG
[0]))
179 T6
.append(mul4(s
, iG
[1]))
180 T7
.append(mul4(s
, iG
[2]))
181 T8
.append(mul4(s
, iG
[3]))
183 U1
.append(mul4(t
, iG
[0]))
184 U2
.append(mul4(t
, iG
[1]))
185 U3
.append(mul4(t
, iG
[2]))
186 U4
.append(mul4(t
, iG
[3]))
191 for t
in xrange(1, 30):
214 def __init__(self
, key
, block_size
= 16):
215 if block_size
!= 16 and block_size
!= 24 and block_size
!= 32:
216 raise ValueError('Invalid block size: ' + str(block_size
))
217 if len(key
) != 16 and len(key
) != 24 and len(key
) != 32:
218 raise ValueError('Invalid key size: ' + str(len(key
)))
219 self
.block_size
= block_size
221 ROUNDS
= num_rounds
[len(key
)][block_size
]
223 # encryption round keys
224 Ke
= [[0] * BC
for i
in xrange(ROUNDS
+ 1)]
225 # decryption round keys
226 Kd
= [[0] * BC
for i
in xrange(ROUNDS
+ 1)]
227 ROUND_KEY_COUNT
= (ROUNDS
+ 1) * BC
230 # copy user material bytes into temporary ints
232 for i
in xrange(0, KC
):
233 tk
.append((ord(key
[i
* 4]) << 24) |
(ord(key
[i
* 4 + 1]) << 16) |
234 (ord(key
[i
* 4 + 2]) << 8) |
ord(key
[i
* 4 + 3]))
236 # copy values into round key arrays
239 while j
< KC
and t
< ROUND_KEY_COUNT
:
240 Ke
[t
/ BC
][t
% BC
] = tk
[j
]
241 Kd
[ROUNDS
- (t
/ BC
)][t
% BC
] = tk
[j
]
246 while t
< ROUND_KEY_COUNT
:
247 # extrapolate using phi (the round key evolution function)
249 tk
[0] ^
= (S
[(tt
>> 16) & 0xFF] & 0xFF) << 24 ^ \
250 (S
[(tt
>> 8) & 0xFF] & 0xFF) << 16 ^ \
251 (S
[ tt
& 0xFF] & 0xFF) << 8 ^ \
252 (S
[(tt
>> 24) & 0xFF] & 0xFF) ^ \
253 (rcon
[rconpointer
] & 0xFF) << 24
256 for i
in xrange(1, KC
):
259 for i
in xrange(1, KC
/ 2):
262 tk
[KC
/ 2] ^
= (S
[ tt
& 0xFF] & 0xFF) ^ \
263 (S
[(tt
>> 8) & 0xFF] & 0xFF) << 8 ^ \
264 (S
[(tt
>> 16) & 0xFF] & 0xFF) << 16 ^ \
265 (S
[(tt
>> 24) & 0xFF] & 0xFF) << 24
266 for i
in xrange(KC
/ 2 + 1, KC
):
268 # copy values into round key arrays
270 while j
< KC
and t
< ROUND_KEY_COUNT
:
271 Ke
[t
/ BC
][t
% BC
] = tk
[j
]
272 Kd
[ROUNDS
- (t
/ BC
)][t
% BC
] = tk
[j
]
275 # inverse MixColumn where needed
276 for r
in xrange(1, ROUNDS
):
279 Kd
[r
][j
] = U1
[(tt
>> 24) & 0xFF] ^ \
280 U2
[(tt
>> 16) & 0xFF] ^ \
281 U3
[(tt
>> 8) & 0xFF] ^ \
286 def encrypt(self
, plaintext
):
287 if len(plaintext
) != self
.block_size
:
288 raise ValueError('wrong block length, expected ' + str(self
.block_size
) + ' got ' + str(len(plaintext
)))
291 BC
= self
.block_size
/ 4
299 s1
= shifts
[SC
][1][0]
300 s2
= shifts
[SC
][2][0]
301 s3
= shifts
[SC
][3][0]
303 # temporary work array
305 # plaintext to ints + key
307 t
.append((ord(plaintext
[i
* 4 ]) << 24 |
308 ord(plaintext
[i
* 4 + 1]) << 16 |
309 ord(plaintext
[i
* 4 + 2]) << 8 |
310 ord(plaintext
[i
* 4 + 3]) ) ^ Ke
[0][i
])
311 # apply round transforms
312 for r
in xrange(1, ROUNDS
):
314 a
[i
] = (T1
[(t
[ i
] >> 24) & 0xFF] ^
315 T2
[(t
[(i
+ s1
) % BC
] >> 16) & 0xFF] ^
316 T3
[(t
[(i
+ s2
) % BC
] >> 8) & 0xFF] ^
317 T4
[ t
[(i
+ s3
) % BC
] & 0xFF] ) ^ Ke
[r
][i
]
319 # last round is special
323 result
.append((S
[(t
[ i
] >> 24) & 0xFF] ^
(tt
>> 24)) & 0xFF)
324 result
.append((S
[(t
[(i
+ s1
) % BC
] >> 16) & 0xFF] ^
(tt
>> 16)) & 0xFF)
325 result
.append((S
[(t
[(i
+ s2
) % BC
] >> 8) & 0xFF] ^
(tt
>> 8)) & 0xFF)
326 result
.append((S
[ t
[(i
+ s3
) % BC
] & 0xFF] ^ tt
) & 0xFF)
327 return string
.join(map(chr, result
), '')
329 def decrypt(self
, ciphertext
):
330 if len(ciphertext
) != self
.block_size
:
331 raise ValueError('wrong block length, expected ' + str(self
.block_size
) + ' got ' + str(len(plaintext
)))
334 BC
= self
.block_size
/ 4
342 s1
= shifts
[SC
][1][1]
343 s2
= shifts
[SC
][2][1]
344 s3
= shifts
[SC
][3][1]
346 # temporary work array
348 # ciphertext to ints + key
350 t
[i
] = (ord(ciphertext
[i
* 4 ]) << 24 |
351 ord(ciphertext
[i
* 4 + 1]) << 16 |
352 ord(ciphertext
[i
* 4 + 2]) << 8 |
353 ord(ciphertext
[i
* 4 + 3]) ) ^ Kd
[0][i
]
354 # apply round transforms
355 for r
in xrange(1, ROUNDS
):
357 a
[i
] = (T5
[(t
[ i
] >> 24) & 0xFF] ^
358 T6
[(t
[(i
+ s1
) % BC
] >> 16) & 0xFF] ^
359 T7
[(t
[(i
+ s2
) % BC
] >> 8) & 0xFF] ^
360 T8
[ t
[(i
+ s3
) % BC
] & 0xFF] ) ^ Kd
[r
][i
]
362 # last round is special
366 result
.append((Si
[(t
[ i
] >> 24) & 0xFF] ^
(tt
>> 24)) & 0xFF)
367 result
.append((Si
[(t
[(i
+ s1
) % BC
] >> 16) & 0xFF] ^
(tt
>> 16)) & 0xFF)
368 result
.append((Si
[(t
[(i
+ s2
) % BC
] >> 8) & 0xFF] ^
(tt
>> 8)) & 0xFF)
369 result
.append((Si
[ t
[(i
+ s3
) % BC
] & 0xFF] ^ tt
) & 0xFF)
370 return string
.join(map(chr, result
), '')
372 def encrypt(key
, block
):
373 return rijndael(key
, len(block
)).encrypt(block
)
375 def decrypt(key
, block
):
376 return rijndael(key
, len(block
)).decrypt(block
)
381 r
= rijndael('a' * kl
, bl
)
382 assert r
.decrypt(r
.encrypt(b
)) == b