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, 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
32 shifts
= [[[0, 0], [1, 3], [2, 2], [3, 1]],
33 [[0, 0], [1, 5], [2, 4], [3, 3]],
34 [[0, 0], [1, 7], [3, 5], [4, 4]]]
36 # [keysize][block_size]
37 num_rounds
= {16: {16: 10, 24: 12, 32: 14}
, 24: {16: 12, 24: 12, 32: 14}
, 32: {16: 14, 24: 14, 32: 14}
}
39 A
= [[1, 1, 1, 1, 1, 0, 0, 0],
40 [0, 1, 1, 1, 1, 1, 0, 0],
41 [0, 0, 1, 1, 1, 1, 1, 0],
42 [0, 0, 0, 1, 1, 1, 1, 1],
43 [1, 0, 0, 0, 1, 1, 1, 1],
44 [1, 1, 0, 0, 0, 1, 1, 1],
45 [1, 1, 1, 0, 0, 0, 1, 1],
46 [1, 1, 1, 1, 0, 0, 0, 1]]
48 # produce log and alog tables, needed for multiplying in the
49 # field GF(2^m) (generator = 3)
52 j
= (alog
[-1] << 1) ^ alog
[-1]
58 for i
in xrange(1, 255):
61 # multiply two elements of GF(2^m)
65 return alog
[(log
[a
& 0xFF] + log
[b
& 0xFF]) % 255]
67 # substitution box based on F^{-1}(x)
68 box
= [[0] * 8 for i
in xrange(256)]
70 for i
in xrange(2, 256):
71 j
= alog
[255 - log
[i
]]
73 box
[i
][t
] = (j
>> (7 - t
)) & 0x01
75 B
= [0, 1, 1, 0, 0, 0, 1, 1]
77 # affine transform: box[i] <- B + A*box[i]
78 cox
= [[0] * 8 for i
in xrange(256)]
83 cox
[i
][t
] ^
= A
[t
][j
] * box
[i
][j
]
85 # S-boxes and inverse S-boxes
90 for t
in xrange(1, 8):
91 S
[i
] ^
= cox
[i
][t
] << (7-t
)
100 AA
= [[0] * 8 for i
in xrange(4)]
111 while AA
[t
][i
] == 0 and t
< 4:
113 assert t
!= 4, 'G matrix must be invertible'
115 AA
[i
][j
], AA
[t
][j
] = AA
[t
][j
], AA
[i
][j
]
119 AA
[i
][j
] = alog
[(255 + log
[AA
[i
][j
] & 0xFF] - log
[pivot
& 0xFF]) % 255]
122 for j
in xrange(i
+1, 8):
123 AA
[t
][j
] ^
= mul(AA
[i
][j
], AA
[t
][i
])
126 iG
= [[0] * 4 for i
in xrange(4)]
130 iG
[i
][j
] = AA
[i
][j
+ 4]
155 for t
in xrange(256):
157 T1
.append(mul4(s
, G
[0]))
158 T2
.append(mul4(s
, G
[1]))
159 T3
.append(mul4(s
, G
[2]))
160 T4
.append(mul4(s
, G
[3]))
163 T5
.append(mul4(s
, iG
[0]))
164 T6
.append(mul4(s
, iG
[1]))
165 T7
.append(mul4(s
, iG
[2]))
166 T8
.append(mul4(s
, iG
[3]))
168 U1
.append(mul4(t
, iG
[0]))
169 U2
.append(mul4(t
, iG
[1]))
170 U3
.append(mul4(t
, iG
[2]))
171 U4
.append(mul4(t
, iG
[3]))
176 for t
in xrange(1, 30):
199 def __init__(self
, key
, block_size
= 16):
200 if block_size
!= 16 and block_size
!= 24 and block_size
!= 32:
201 raise ValueError('Invalid block size: ' + str(block_size
))
202 if len(key
) != 16 and len(key
) != 24 and len(key
) != 32:
203 raise ValueError('Invalid key size: ' + str(len(key
)))
204 self
.block_size
= block_size
206 ROUNDS
= num_rounds
[len(key
)][block_size
]
208 # encryption round keys
209 Ke
= [[0] * BC
for i
in xrange(ROUNDS
+ 1)]
210 # decryption round keys
211 Kd
= [[0] * BC
for i
in xrange(ROUNDS
+ 1)]
212 ROUND_KEY_COUNT
= (ROUNDS
+ 1) * BC
215 # copy user material bytes into temporary ints
217 for i
in xrange(0, KC
):
218 tk
.append((ord(key
[i
* 4]) << 24) |
(ord(key
[i
* 4 + 1]) << 16) |
219 (ord(key
[i
* 4 + 2]) << 8) |
ord(key
[i
* 4 + 3]))
221 # copy values into round key arrays
224 while j
< KC
and t
< ROUND_KEY_COUNT
:
225 Ke
[t
/ BC
][t
% BC
] = tk
[j
]
226 Kd
[ROUNDS
- (t
/ BC
)][t
% BC
] = tk
[j
]
231 while t
< ROUND_KEY_COUNT
:
232 # extrapolate using phi (the round key evolution function)
234 tk
[0] ^
= (S
[(tt
>> 16) & 0xFF] & 0xFF) << 24 ^ \
235 (S
[(tt
>> 8) & 0xFF] & 0xFF) << 16 ^ \
236 (S
[ tt
& 0xFF] & 0xFF) << 8 ^ \
237 (S
[(tt
>> 24) & 0xFF] & 0xFF) ^ \
238 (rcon
[rconpointer
] & 0xFF) << 24
241 for i
in xrange(1, KC
):
244 for i
in xrange(1, KC
/ 2):
247 tk
[KC
/ 2] ^
= (S
[ tt
& 0xFF] & 0xFF) ^ \
248 (S
[(tt
>> 8) & 0xFF] & 0xFF) << 8 ^ \
249 (S
[(tt
>> 16) & 0xFF] & 0xFF) << 16 ^ \
250 (S
[(tt
>> 24) & 0xFF] & 0xFF) << 24
251 for i
in xrange(KC
/ 2 + 1, KC
):
253 # copy values into round key arrays
255 while j
< KC
and t
< ROUND_KEY_COUNT
:
256 Ke
[t
/ BC
][t
% BC
] = tk
[j
]
257 Kd
[ROUNDS
- (t
/ BC
)][t
% BC
] = tk
[j
]
260 # inverse MixColumn where needed
261 for r
in xrange(1, ROUNDS
):
264 Kd
[r
][j
] = U1
[(tt
>> 24) & 0xFF] ^ \
265 U2
[(tt
>> 16) & 0xFF] ^ \
266 U3
[(tt
>> 8) & 0xFF] ^ \
271 def encrypt(self
, plaintext
):
272 if len(plaintext
) != self
.block_size
:
273 raise ValueError('wrong block length, expected ' + str(self
.block_size
) + ' got ' + str(len(plaintext
)))
276 BC
= self
.block_size
/ 4
284 s1
= shifts
[SC
][1][0]
285 s2
= shifts
[SC
][2][0]
286 s3
= shifts
[SC
][3][0]
288 # temporary work array
290 # plaintext to ints + key
292 t
.append((ord(plaintext
[i
* 4 ]) << 24 |
293 ord(plaintext
[i
* 4 + 1]) << 16 |
294 ord(plaintext
[i
* 4 + 2]) << 8 |
295 ord(plaintext
[i
* 4 + 3]) ) ^ Ke
[0][i
])
296 # apply round transforms
297 for r
in xrange(1, ROUNDS
):
299 a
[i
] = (T1
[(t
[ i
] >> 24) & 0xFF] ^
300 T2
[(t
[(i
+ s1
) % BC
] >> 16) & 0xFF] ^
301 T3
[(t
[(i
+ s2
) % BC
] >> 8) & 0xFF] ^
302 T4
[ t
[(i
+ s3
) % BC
] & 0xFF] ) ^ Ke
[r
][i
]
304 # last round is special
308 result
.append((S
[(t
[ i
] >> 24) & 0xFF] ^
(tt
>> 24)) & 0xFF)
309 result
.append((S
[(t
[(i
+ s1
) % BC
] >> 16) & 0xFF] ^
(tt
>> 16)) & 0xFF)
310 result
.append((S
[(t
[(i
+ s2
) % BC
] >> 8) & 0xFF] ^
(tt
>> 8)) & 0xFF)
311 result
.append((S
[ t
[(i
+ s3
) % BC
] & 0xFF] ^ tt
) & 0xFF)
312 return string
.join(map(chr, result
), '')
314 def decrypt(self
, ciphertext
):
315 if len(ciphertext
) != self
.block_size
:
316 raise ValueError('wrong block length, expected ' + str(self
.block_size
) + ' got ' + str(len(ciphertext
)))
319 BC
= self
.block_size
/ 4
327 s1
= shifts
[SC
][1][1]
328 s2
= shifts
[SC
][2][1]
329 s3
= shifts
[SC
][3][1]
331 # temporary work array
333 # ciphertext to ints + key
335 t
[i
] = (ord(ciphertext
[i
* 4 ]) << 24 |
336 ord(ciphertext
[i
* 4 + 1]) << 16 |
337 ord(ciphertext
[i
* 4 + 2]) << 8 |
338 ord(ciphertext
[i
* 4 + 3]) ) ^ Kd
[0][i
]
339 # apply round transforms
340 for r
in xrange(1, ROUNDS
):
342 a
[i
] = (T5
[(t
[ i
] >> 24) & 0xFF] ^
343 T6
[(t
[(i
+ s1
) % BC
] >> 16) & 0xFF] ^
344 T7
[(t
[(i
+ s2
) % BC
] >> 8) & 0xFF] ^
345 T8
[ t
[(i
+ s3
) % BC
] & 0xFF] ) ^ Kd
[r
][i
]
347 # last round is special
351 result
.append((Si
[(t
[ i
] >> 24) & 0xFF] ^
(tt
>> 24)) & 0xFF)
352 result
.append((Si
[(t
[(i
+ s1
) % BC
] >> 16) & 0xFF] ^
(tt
>> 16)) & 0xFF)
353 result
.append((Si
[(t
[(i
+ s2
) % BC
] >> 8) & 0xFF] ^
(tt
>> 8)) & 0xFF)
354 result
.append((Si
[ t
[(i
+ s3
) % BC
] & 0xFF] ^ tt
) & 0xFF)
355 return string
.join(map(chr, result
), '')
357 def encrypt(key
, block
):
358 return rijndael(key
, len(block
)).encrypt(block
)
360 def decrypt(key
, block
):
361 return rijndael(key
, len(block
)).decrypt(block
)
366 r
= rijndael('a' * kl
, bl
)
367 assert r
.decrypt(r
.encrypt(b
)) == b