rijndael.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. """
  2. Copyright (c) 2014 Philippe Teuwen <phil@teuwen.org>
  3. Permission is hereby granted, free of charge, to any person obtaining a copy of
  4. this software and associated documentation files (the "Software"), to deal in
  5. the Software without restriction, including without limitation the rights to
  6. use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  7. of the Software, and to permit persons to whom the Software is furnished to do
  8. so, subject to the following conditions:
  9. The above copyright notice and this permission notice shall be included in all
  10. copies or substantial portions of the Software.
  11. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  16. FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  17. IN THE SOFTWARE.
  18. This code is taken from https://github.com/doegox/python-cryptoplus/
  19. A pure python (slow) implementation of rijndael with a decent interface
  20. To include -
  21. from rijndael import rijndael
  22. To do a key setup -
  23. r = rijndael(key, block_size = 16)
  24. key must be a string of length 16, 24, or 32
  25. blocksize must be 16, 24, or 32. Default is 16
  26. To use -
  27. ciphertext = r.encrypt(plaintext)
  28. plaintext = r.decrypt(ciphertext)
  29. If any strings are of the wrong length a ValueError is thrown
  30. """
  31. # ported from the Java reference code by Bram Cohen, bram@gawth.com, April 2001
  32. # this code is public domain, unless someone makes
  33. # an intellectual property claim against the reference
  34. # code, in which case it can be made public domain by
  35. # deleting all the comments and renaming all the variables
  36. import copy
  37. #-----------------------
  38. #TREV - ADDED BECAUSE THERE'S WARNINGS ABOUT INT OVERFLOW BEHAVIOR CHANGING IN
  39. #2.4.....
  40. #import os
  41. #if os.name != "java":
  42. # import exceptions
  43. # if hasattr(exceptions, "FutureWarning"):
  44. # import warnings
  45. # warnings.filterwarnings("ignore", category=FutureWarning, append=1)
  46. #-----------------------
  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]]]
  50. # [keysize][block_size]
  51. num_rounds = {16: {16: 10, 24: 12, 32: 14}, 24: {16: 12, 24: 12, 32: 14}, 32: {16: 14, 24: 14, 32: 14}}
  52. A = [[1, 1, 1, 1, 1, 0, 0, 0],
  53. [0, 1, 1, 1, 1, 1, 0, 0],
  54. [0, 0, 1, 1, 1, 1, 1, 0],
  55. [0, 0, 0, 1, 1, 1, 1, 1],
  56. [1, 0, 0, 0, 1, 1, 1, 1],
  57. [1, 1, 0, 0, 0, 1, 1, 1],
  58. [1, 1, 1, 0, 0, 0, 1, 1],
  59. [1, 1, 1, 1, 0, 0, 0, 1]]
  60. # produce log and alog tables, needed for multiplying in the
  61. # field GF(2^m) (generator = 3)
  62. alog = [1]
  63. for i in range(255):
  64. j = (alog[-1] << 1) ^ alog[-1]
  65. if j & 0x100 != 0:
  66. j ^= 0x11B
  67. alog.append(j)
  68. log = [0] * 256
  69. for i in range(1, 255):
  70. log[alog[i]] = i
  71. # multiply two elements of GF(2^m)
  72. def mul(a, b):
  73. if a == 0 or b == 0:
  74. return 0
  75. return alog[(log[a & 0xFF] + log[b & 0xFF]) % 255]
  76. # substitution box based on F^{-1}(x)
  77. box = [[0] * 8 for i in range(256)]
  78. box[1][7] = 1
  79. for i in range(2, 256):
  80. j = alog[255 - log[i]]
  81. for t in range(8):
  82. box[i][t] = (j >> (7 - t)) & 0x01
  83. B = [0, 1, 1, 0, 0, 0, 1, 1]
  84. # affine transform: box[i] <- B + A*box[i]
  85. cox = [[0] * 8 for i in range(256)]
  86. for i in range(256):
  87. for t in range(8):
  88. cox[i][t] = B[t]
  89. for j in range(8):
  90. cox[i][t] ^= A[t][j] * box[i][j]
  91. # S-boxes and inverse S-boxes
  92. S = [0] * 256
  93. Si = [0] * 256
  94. for i in range(256):
  95. S[i] = cox[i][0] << 7
  96. for t in range(1, 8):
  97. S[i] ^= cox[i][t] << (7-t)
  98. Si[S[i] & 0xFF] = i
  99. # T-boxes
  100. G = [[2, 1, 1, 3],
  101. [3, 2, 1, 1],
  102. [1, 3, 2, 1],
  103. [1, 1, 3, 2]]
  104. AA = [[0] * 8 for i in range(4)]
  105. for i in range(4):
  106. for j in range(4):
  107. AA[i][j] = G[i][j]
  108. AA[i][i+4] = 1
  109. for i in range(4):
  110. pivot = AA[i][i]
  111. if pivot == 0:
  112. t = i + 1
  113. while AA[t][i] == 0 and t < 4:
  114. t += 1
  115. assert t != 4, 'G matrix must be invertible'
  116. for j in range(8):
  117. AA[i][j], AA[t][j] = AA[t][j], AA[i][j]
  118. pivot = AA[i][i]
  119. for j in range(8):
  120. if AA[i][j] != 0:
  121. AA[i][j] = alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) % 255]
  122. for t in range(4):
  123. if i != t:
  124. for j in range(i+1, 8):
  125. AA[t][j] ^= mul(AA[i][j], AA[t][i])
  126. AA[t][i] = 0
  127. iG = [[0] * 4 for i in range(4)]
  128. for i in range(4):
  129. for j in range(4):
  130. iG[i][j] = AA[i][j + 4]
  131. def mul4(a, bs):
  132. if a == 0:
  133. return 0
  134. r = 0
  135. for b in bs:
  136. r <<= 8
  137. if b != 0:
  138. r = r | mul(a, b)
  139. return r
  140. T1 = []
  141. T2 = []
  142. T3 = []
  143. T4 = []
  144. T5 = []
  145. T6 = []
  146. T7 = []
  147. T8 = []
  148. U1 = []
  149. U2 = []
  150. U3 = []
  151. U4 = []
  152. for t in range(256):
  153. s = S[t]
  154. T1.append(mul4(s, G[0]))
  155. T2.append(mul4(s, G[1]))
  156. T3.append(mul4(s, G[2]))
  157. T4.append(mul4(s, G[3]))
  158. s = Si[t]
  159. T5.append(mul4(s, iG[0]))
  160. T6.append(mul4(s, iG[1]))
  161. T7.append(mul4(s, iG[2]))
  162. T8.append(mul4(s, iG[3]))
  163. U1.append(mul4(t, iG[0]))
  164. U2.append(mul4(t, iG[1]))
  165. U3.append(mul4(t, iG[2]))
  166. U4.append(mul4(t, iG[3]))
  167. # round constants
  168. rcon = [1]
  169. r = 1
  170. for t in range(1, 30):
  171. r = mul(2, r)
  172. rcon.append(r)
  173. del A
  174. del AA
  175. del pivot
  176. del B
  177. del G
  178. del box
  179. del log
  180. del alog
  181. del i
  182. del j
  183. del r
  184. del s
  185. del t
  186. del mul
  187. del mul4
  188. del cox
  189. del iG
  190. class rijndael:
  191. def __init__(self, key, block_size = 16):
  192. if block_size != 16 and block_size != 24 and block_size != 32:
  193. raise ValueError('Invalid block size: ' + str(block_size))
  194. if len(key) != 16 and len(key) != 24 and len(key) != 32:
  195. raise ValueError('Invalid key size: ' + str(len(key)))
  196. self.block_size = block_size
  197. ROUNDS = num_rounds[len(key)][block_size]
  198. BC = block_size // 4
  199. # encryption round keys
  200. Ke = [[0] * BC for i in range(ROUNDS + 1)]
  201. # decryption round keys
  202. Kd = [[0] * BC for i in range(ROUNDS + 1)]
  203. ROUND_KEY_COUNT = (ROUNDS + 1) * BC
  204. KC = len(key) // 4
  205. # copy user material bytes into temporary ints
  206. tk = []
  207. for i in range(0, KC):
  208. tk.append((ord(key[i * 4]) << 24) | (ord(key[i * 4 + 1]) << 16) |
  209. (ord(key[i * 4 + 2]) << 8) | ord(key[i * 4 + 3]))
  210. # copy values into round key arrays
  211. t = 0
  212. j = 0
  213. while j < KC and t < ROUND_KEY_COUNT:
  214. Ke[t // BC][t % BC] = tk[j]
  215. Kd[ROUNDS - (t // BC)][t % BC] = tk[j]
  216. j += 1
  217. t += 1
  218. tt = 0
  219. rconpointer = 0
  220. while t < ROUND_KEY_COUNT:
  221. # extrapolate using phi (the round key evolution function)
  222. tt = tk[KC - 1]
  223. tk[0] ^= (S[(tt >> 16) & 0xFF] & 0xFF) << 24 ^ \
  224. (S[(tt >> 8) & 0xFF] & 0xFF) << 16 ^ \
  225. (S[ tt & 0xFF] & 0xFF) << 8 ^ \
  226. (S[(tt >> 24) & 0xFF] & 0xFF) ^ \
  227. (rcon[rconpointer] & 0xFF) << 24
  228. rconpointer += 1
  229. if KC != 8:
  230. for i in range(1, KC):
  231. tk[i] ^= tk[i-1]
  232. else:
  233. for i in range(1, KC // 2):
  234. tk[i] ^= tk[i-1]
  235. tt = tk[KC // 2 - 1]
  236. tk[KC // 2] ^= (S[ tt & 0xFF] & 0xFF) ^ \
  237. (S[(tt >> 8) & 0xFF] & 0xFF) << 8 ^ \
  238. (S[(tt >> 16) & 0xFF] & 0xFF) << 16 ^ \
  239. (S[(tt >> 24) & 0xFF] & 0xFF) << 24
  240. for i in range(KC // 2 + 1, KC):
  241. tk[i] ^= tk[i-1]
  242. # copy values into round key arrays
  243. j = 0
  244. while j < KC and t < ROUND_KEY_COUNT:
  245. Ke[t // BC][t % BC] = tk[j]
  246. Kd[ROUNDS - (t // BC)][t % BC] = tk[j]
  247. j += 1
  248. t += 1
  249. # inverse MixColumn where needed
  250. for r in range(1, ROUNDS):
  251. for j in range(BC):
  252. tt = Kd[r][j]
  253. Kd[r][j] = U1[(tt >> 24) & 0xFF] ^ \
  254. U2[(tt >> 16) & 0xFF] ^ \
  255. U3[(tt >> 8) & 0xFF] ^ \
  256. U4[ tt & 0xFF]
  257. self.Ke = Ke
  258. self.Kd = Kd
  259. def encrypt(self, plaintext):
  260. if len(plaintext) != self.block_size:
  261. raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(plaintext)))
  262. Ke = self.Ke
  263. BC = self.block_size // 4
  264. ROUNDS = len(Ke) - 1
  265. if BC == 4:
  266. SC = 0
  267. elif BC == 6:
  268. SC = 1
  269. else:
  270. SC = 2
  271. s1 = shifts[SC][1][0]
  272. s2 = shifts[SC][2][0]
  273. s3 = shifts[SC][3][0]
  274. a = [0] * BC
  275. # temporary work array
  276. t = []
  277. # plaintext to ints + key
  278. for i in range(BC):
  279. t.append((ord(plaintext[i * 4 ]) << 24 |
  280. ord(plaintext[i * 4 + 1]) << 16 |
  281. ord(plaintext[i * 4 + 2]) << 8 |
  282. ord(plaintext[i * 4 + 3]) ) ^ Ke[0][i])
  283. # apply round transforms
  284. for r in range(1, ROUNDS):
  285. for i in range(BC):
  286. a[i] = (T1[(t[ i ] >> 24) & 0xFF] ^
  287. T2[(t[(i + s1) % BC] >> 16) & 0xFF] ^
  288. T3[(t[(i + s2) % BC] >> 8) & 0xFF] ^
  289. T4[ t[(i + s3) % BC] & 0xFF] ) ^ Ke[r][i]
  290. t = copy.copy(a)
  291. # last round is special
  292. result = []
  293. for i in range(BC):
  294. tt = Ke[ROUNDS][i]
  295. result.append((S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
  296. result.append((S[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
  297. result.append((S[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
  298. result.append((S[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF)
  299. return "".join(list(map(chr, result)))
  300. def decrypt(self, ciphertext):
  301. if len(ciphertext) != self.block_size:
  302. raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(plaintext)))
  303. Kd = self.Kd
  304. BC = self.block_size // 4
  305. ROUNDS = len(Kd) - 1
  306. if BC == 4:
  307. SC = 0
  308. elif BC == 6:
  309. SC = 1
  310. else:
  311. SC = 2
  312. s1 = shifts[SC][1][1]
  313. s2 = shifts[SC][2][1]
  314. s3 = shifts[SC][3][1]
  315. a = [0] * BC
  316. # temporary work array
  317. t = [0] * BC
  318. # ciphertext to ints + key
  319. for i in range(BC):
  320. t[i] = (ord(ciphertext[i * 4 ]) << 24 |
  321. ord(ciphertext[i * 4 + 1]) << 16 |
  322. ord(ciphertext[i * 4 + 2]) << 8 |
  323. ord(ciphertext[i * 4 + 3]) ) ^ Kd[0][i]
  324. # apply round transforms
  325. for r in range(1, ROUNDS):
  326. for i in range(BC):
  327. a[i] = (T5[(t[ i ] >> 24) & 0xFF] ^
  328. T6[(t[(i + s1) % BC] >> 16) & 0xFF] ^
  329. T7[(t[(i + s2) % BC] >> 8) & 0xFF] ^
  330. T8[ t[(i + s3) % BC] & 0xFF] ) ^ Kd[r][i]
  331. t = copy.copy(a)
  332. # last round is special
  333. result = []
  334. for i in range(BC):
  335. tt = Kd[ROUNDS][i]
  336. result.append((Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
  337. result.append((Si[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
  338. result.append((Si[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
  339. result.append((Si[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF)
  340. return "".join(list(map(chr, result)))
  341. def encrypt(key, block):
  342. return rijndael(key, len(block)).encrypt(block)
  343. def decrypt(key, block):
  344. return rijndael(key, len(block)).decrypt(block)
  345. if __name__ == '__main__':
  346. # test
  347. def t(kl, bl):
  348. b = 'b' * bl
  349. r = rijndael('a' * kl, bl)
  350. assert r.decrypt(r.encrypt(b)) == b
  351. t(16, 16)
  352. t(16, 24)
  353. t(16, 32)
  354. t(24, 16)
  355. t(24, 24)
  356. t(24, 32)
  357. t(32, 16)
  358. t(32, 24)
  359. t(32, 32)