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