Utilities/crypto/py3AES.py

changeset 1127
b1802ebe0066
child 1509
c0b5e693b0eb
equal deleted inserted replaced
1123:4ed80ed7e433 1127:b1802ebe0066
1 #!/usr/bin/python3
2 #
3 # aes.py: implements AES - Advanced Encryption Standard
4 # from the SlowAES project, http://code.google.com/p/slowaes/
5 #
6 # Copyright (c) 2008 Josh Davis ( http://www.josh-davis.org ),
7 # Alex Martelli ( http://www.aleax.it )
8 #
9 # Ported from C code written by Laurent Haan ( http://www.progressive-coding.com )
10 #
11 # Licensed under the Apache License, Version 2.0
12 # http://www.apache.org/licenses/
13 #
14
15 #
16 # Ported to Python3
17 #
18 # Copyright (c) 2011 Detlev Offenbach <detlev@die-offenbachs.de>
19 #
20
21 """
22 Module implementing classes for encryption according
23 Advanced Encryption Standard.
24 """
25
26 import os
27 import math
28
29
30 def append_PKCS7_padding(b):
31 """
32 Function to pad the given data to a multiple of 16-bytes by PKCS7 padding.
33
34 @param b data to be padded (bytes)
35 @return padded data (bytes)
36 """
37 numpads = 16 - (len(b) % 16)
38 return b + numpads * bytes(chr(numpads), encoding="ascii")
39
40
41 def strip_PKCS7_padding(b):
42 """
43 Function to strip off PKCS7 padding.
44
45 @param b data to be stripped (bytes)
46 @return stripped data (bytes)
47 """
48 if len(b) % 16 or not b:
49 raise ValueError("Data of len {0} can't be PCKS7-padded".format(len(b)))
50 numpads = b[-1]
51 if numpads > 16:
52 raise ValueError("Data ending with {0} can't be PCKS7-padded".format(b[-1]))
53 return b[:-numpads]
54
55
56 class AES(object):
57 """
58 Class implementing the Advanced Encryption Standard algorithm.
59 """
60 # valid key sizes
61 KeySize = {
62 "SIZE_128": 16,
63 "SIZE_192": 24,
64 "SIZE_256": 32,
65 }
66
67 # Rijndael S-box
68 sbox = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67,
69 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59,
70 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7,
71 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1,
72 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05,
73 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83,
74 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29,
75 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
76 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa,
77 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c,
78 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc,
79 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
80 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19,
81 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee,
82 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49,
83 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
84 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4,
85 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6,
86 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70,
87 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9,
88 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e,
89 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1,
90 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0,
91 0x54, 0xbb, 0x16]
92
93 # Rijndael Inverted S-box
94 rsbox = [0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3,
95 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f,
96 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54,
97 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b,
98 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24,
99 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8,
100 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d,
101 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
102 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab,
103 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3,
104 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1,
105 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41,
106 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6,
107 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9,
108 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d,
109 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
110 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0,
111 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07,
112 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60,
113 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f,
114 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5,
115 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b,
116 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55,
117 0x21, 0x0c, 0x7d]
118
119 # Rijndael Rcon
120 Rcon = [0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
121 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97,
122 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72,
123 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66,
124 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
125 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d,
126 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
127 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61,
128 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
129 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40,
130 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc,
131 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5,
132 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a,
133 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d,
134 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
135 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
136 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4,
137 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
138 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08,
139 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
140 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d,
141 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2,
142 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74,
143 0xe8, 0xcb]
144
145 def __getSBoxValue(self, num):
146 """
147 Private method to retrieve a given S-Box value.
148
149 @param num position of the value (integer)
150 @return value of the S-Box (integer)
151 """
152 return self.sbox[num]
153
154 def __getSBoxInvert(self, num):
155 """
156 Private method to retrieve a given Inverted S-Box value.
157
158 @param num position of the value (integer)
159 @return value of the Inverted S-Box (integer)
160 """
161 return self.rsbox[num]
162
163 def __rotate(self, data):
164 """
165 Private method performing Rijndael's key schedule rotate operation.
166
167 Rotate the data word eight bits to the left: eg, rotate(1d2c3a4f) == 2c3a4f1d.
168
169 @param data data of size 4 (bytearray)
170 """
171 return data[1:] + data[:1]
172
173 def __getRconValue(self, num):
174 """
175 Private method to retrieve a given Rcon value.
176
177 @param num position of the value (integer)
178 @return Rcon value (integer)
179 """
180 return self.Rcon[num]
181
182 def __core(self, data, iteration):
183 """
184 Private method performing the key schedule core operation.
185
186 @param data data to operate on (bytearray)
187 @param iteration iteration counter (integer)
188 @return modified data (bytearray)
189 """
190 # rotate the 32-bit word 8 bits to the left
191 data = self.__rotate(data)
192 # apply S-Box substitution on all 4 parts of the 32-bit word
193 for i in range(4):
194 data[i] = self.__getSBoxValue(data[i])
195 # XOR the output of the rcon operation with i to the first part
196 # (leftmost) only
197 data[0] = data[0] ^ self.__getRconValue(iteration)
198 return data
199
200 def __expandKey(self, key, size, expandedKeySize):
201 """
202 Private method performing Rijndael's key expansion.
203
204 Expands a 128, 192 or 256 bit key into a 176, 208 or 240 bit key.
205
206 @param key key to be expanded (bytes or bytearray)
207 @param size size of the key in bytes (16, 24 or 32)
208 @param expandedKeySize size of the expanded key (integer)
209 @return expanded key (bytearray)
210 """
211 # current expanded keySize, in bytes
212 currentSize = 0
213 rconIteration = 1
214 expandedKey = bytearray(expandedKeySize)
215
216 # set the 16, 24, 32 bytes of the expanded key to the input key
217 for j in range(size):
218 expandedKey[j] = key[j]
219 currentSize += size
220
221 while currentSize < expandedKeySize:
222 # assign the previous 4 bytes to the temporary value t
223 t = expandedKey[currentSize - 4:currentSize]
224
225 # every 16, 24, 32 bytes we apply the core schedule to t
226 # and increment rconIteration afterwards
227 if currentSize % size == 0:
228 t = self.__core(t, rconIteration)
229 rconIteration += 1
230 # For 256-bit keys, we add an extra sbox to the calculation
231 if size == self.KeySize["SIZE_256"] and ((currentSize % size) == 16):
232 for l in range(4):
233 t[l] = self.__getSBoxValue(t[l])
234
235 # We XOR t with the four-byte block 16, 24, 32 bytes before the new
236 # expanded key. This becomes the next four bytes in the expanded key.
237 for m in range(4):
238 expandedKey[currentSize] = \
239 expandedKey[currentSize - size] ^ t[m]
240 currentSize += 1
241
242 return expandedKey
243
244 def __addRoundKey(self, state, roundKey):
245 """
246 Private method to add (XORs) the round key to the state.
247
248 @param state state to be changed (bytearray)
249 @param roundKey key to be used for the modification (bytearray)
250 @return modified state (bytearray)
251 """
252 buf = state[:]
253 for i in range(16):
254 buf[i] ^= roundKey[i]
255 return buf
256
257 def __createRoundKey(self, expandedKey, roundKeyPointer):
258 """
259 Private method to create a round key.
260
261 @param expandedKey expanded key to be used (bytearray)
262 @param roundKeyPointer position within the expanded key (integer)
263 @return round key (bytearray)
264 """
265 roundKey = bytearray(16)
266 for i in range(4):
267 for j in range(4):
268 roundKey[j * 4 + i] = expandedKey[roundKeyPointer + i * 4 + j]
269 return roundKey
270
271 def __galois_multiplication(self, a, b):
272 """
273 Private method to perform a Galois multiplication of 8 bit characters a and b.
274
275 @param a first factor (byte)
276 @param b second factor (byte)
277 @return result (byte)
278 """
279 p = 0
280 for counter in range(8):
281 if b & 1:
282 p ^= a
283 hi_bit_set = a & 0x80
284 a <<= 1
285 # keep a 8 bit
286 a &= 0xFF
287 if hi_bit_set:
288 a ^= 0x1b
289 b >>= 1
290 return p
291
292 def __subBytes(self, state, isInv):
293 """
294 Private method to substitute all the values from the state with the value in
295 the SBox using the state value as index for the SBox.
296
297 @param state state to be worked on (bytearray)
298 @param isInv flag indicating an inverse operation (boolean)
299 @return modified state (bytearray)
300 """
301 state = state[:]
302 if isInv:
303 getter = self.__getSBoxInvert
304 else:
305 getter = self.__getSBoxValue
306 for i in range(16):
307 state[i] = getter(state[i])
308 return state
309
310 def __shiftRows(self, state, isInv):
311 """
312 Private method to iterate over the 4 rows and call __shiftRow() with
313 that row.
314
315 @param state state to be worked on (bytearray)
316 @param isInv flag indicating an inverse operation (boolean)
317 @return modified state (bytearray)
318 """
319 state = state[:]
320 for i in range(4):
321 state = self.__shiftRow(state, i * 4, i, isInv)
322 return state
323
324 def __shiftRow(self, state, statePointer, nbr, isInv):
325 """
326 Private method to shift the bytes of a row to the left.
327
328 @param state state to be worked on (bytearray)
329 @param statePointer index into the state (integer)
330 @param nbr number of positions to shift (integer)
331 @param isInv flag indicating an inverse operation (boolean)
332 @return modified state (bytearray)
333 """
334 state = state[:]
335 for i in range(nbr):
336 if isInv:
337 state[statePointer:statePointer + 4] = \
338 state[statePointer + 3:statePointer + 4] + \
339 state[statePointer:statePointer + 3]
340 else:
341 state[statePointer:statePointer + 4] = \
342 state[statePointer + 1:statePointer + 4] + \
343 state[statePointer:statePointer + 1]
344 return state
345
346 def __mixColumns(self, state, isInv):
347 """
348 Private method to perform a galois multiplication of the 4x4 matrix.
349
350 @param state state to be worked on (bytearray)
351 @param isInv flag indicating an inverse operation (boolean)
352 @return modified state (bytearray)
353 """
354 state = state[:]
355 # iterate over the 4 columns
356 for i in range(4):
357 # construct one column by slicing over the 4 rows
358 column = state[i:i + 16:4]
359 # apply the __mixColumn on one column
360 column = self.__mixColumn(column, isInv)
361 # put the values back into the state
362 state[i:i + 16:4] = column
363
364 return state
365
366 # galois multiplication of 1 column of the 4x4 matrix
367 def __mixColumn(self, column, isInv):
368 """
369 Private method to perform a galois multiplication of 1 column the 4x4 matrix.
370
371 @param column column to be worked on (bytearray)
372 @param isInv flag indicating an inverse operation (boolean)
373 @return modified column (bytearray)
374 """
375 column = column[:]
376 if isInv:
377 mult = [14, 9, 13, 11]
378 else:
379 mult = [2, 1, 1, 3]
380 cpy = column[:]
381 g = self.__galois_multiplication
382
383 column[0] = g(cpy[0], mult[0]) ^ g(cpy[3], mult[1]) ^ \
384 g(cpy[2], mult[2]) ^ g(cpy[1], mult[3])
385 column[1] = g(cpy[1], mult[0]) ^ g(cpy[0], mult[1]) ^ \
386 g(cpy[3], mult[2]) ^ g(cpy[2], mult[3])
387 column[2] = g(cpy[2], mult[0]) ^ g(cpy[1], mult[1]) ^ \
388 g(cpy[0], mult[2]) ^ g(cpy[3], mult[3])
389 column[3] = g(cpy[3], mult[0]) ^ g(cpy[2], mult[1]) ^ \
390 g(cpy[1], mult[2]) ^ g(cpy[0], mult[3])
391 return column
392
393 def __aes_round(self, state, roundKey):
394 """
395 Private method to apply the 4 operations of the forward round in sequence.
396
397 @param state state to be worked on (bytearray)
398 @param roundKey round key to be used (bytearray)
399 @return modified state (bytearray)
400 """
401 state = self.__subBytes(state, False)
402 state = self.__shiftRows(state, False)
403 state = self.__mixColumns(state, False)
404 state = self.__addRoundKey(state, roundKey)
405 return state
406
407 def __aes_invRound(self, state, roundKey):
408 """
409 Private method to apply the 4 operations of the inverse round in sequence.
410
411 @param state state to be worked on (bytearray)
412 @param roundKey round key to be used (bytearray)
413 @return modified state (bytearray)
414 """
415 state = self.__shiftRows(state, True)
416 state = self.__subBytes(state, True)
417 state = self.__addRoundKey(state, roundKey)
418 state = self.__mixColumns(state, True)
419 return state
420
421 def __aes_main(self, state, expandedKey, nbrRounds):
422 """
423 Private method to perform the initial operations, the standard round, and the
424 final operations of the forward AES, creating a round key for each round.
425
426 @param state state to be worked on (bytearray)
427 @param expandedKey expanded key to be used (bytearray)
428 @param nbrRounds number of rounds to be done (integer)
429 @return modified state (bytearray)
430 """
431 state = self.__addRoundKey(state, self.__createRoundKey(expandedKey, 0))
432 i = 1
433 while i < nbrRounds:
434 state = self.__aes_round(
435 state, self.__createRoundKey(expandedKey, 16 * i))
436 i += 1
437 state = self.__subBytes(state, False)
438 state = self.__shiftRows(state, False)
439 state = self.__addRoundKey(
440 state, self.__createRoundKey(expandedKey, 16 * nbrRounds))
441 return state
442
443 def __aes_invMain(self, state, expandedKey, nbrRounds):
444 """
445 Private method to perform the initial operations, the standard round, and the
446 final operations of the inverse AES, creating a round key for each round.
447
448 @param state state to be worked on (bytearray)
449 @param expandedKey expanded key to be used (bytearray)
450 @param nbrRounds number of rounds to be done (integer)
451 @return modified state (bytearray)
452 """
453 state = self.__addRoundKey(
454 state, self.__createRoundKey(expandedKey, 16 * nbrRounds))
455 i = nbrRounds - 1
456 while i > 0:
457 state = self.__aes_invRound(
458 state, self.__createRoundKey(expandedKey, 16 * i))
459 i -= 1
460 state = self.__shiftRows(state, True)
461 state = self.__subBytes(state, True)
462 state = self.__addRoundKey(state, self.__createRoundKey(expandedKey, 0))
463 return state
464
465 def encrypt(self, iput, key, size):
466 """
467 Public method to encrypt a 128 bit input block against the given key of size
468 specified.
469
470 @param iput input data (bytearray)
471 @param key key to be used (bytes or bytearray)
472 @param size key size (16, 24 or 32)
473 @return encrypted data (bytes)
474 """
475 output = bytearray(16)
476 # the number of rounds
477 nbrRounds = 0
478 # the 128 bit block to encode
479 block = bytearray(16)
480 # set the number of rounds
481 if size == self.KeySize["SIZE_128"]:
482 nbrRounds = 10
483 elif size == self.KeySize["SIZE_192"]:
484 nbrRounds = 12
485 elif size == self.KeySize["SIZE_256"]:
486 nbrRounds = 14
487 else:
488 raise ValueError("Wrong key size given ({0}).".format(size))
489
490 # the expanded keySize
491 expandedKeySize = 16 * (nbrRounds + 1)
492
493 # Set the block values, for the block:
494 # a0,0 a0,1 a0,2 a0,3
495 # a1,0 a1,1 a1,2 a1,3
496 # a2,0 a2,1 a2,2 a2,3
497 # a3,0 a3,1 a3,2 a3,3
498 # the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3
499 #
500 # iterate over the columns
501 for i in range(4):
502 # iterate over the rows
503 for j in range(4):
504 block[i + j * 4] = iput[i * 4 + j]
505
506 # expand the key into an 176, 208, 240 bytes key
507 # the expanded key
508 expandedKey = self.__expandKey(key, size, expandedKeySize)
509
510 # encrypt the block using the expandedKey
511 block = self.__aes_main(block, expandedKey, nbrRounds)
512
513 # unmap the block again into the output
514 for k in range(4):
515 # iterate over the rows
516 for l in range(4):
517 output[k * 4 + l] = block[k + l * 4]
518 return bytes(output)
519
520 # decrypts a 128 bit input block against the given key of size specified
521 def decrypt(self, iput, key, size):
522 """
523 Public method to decrypt a 128 bit input block against the given key of size
524 specified.
525
526 @param iput input data (bytearray)
527 @param key key to be used (bytes or bytearray)
528 @param size key size (16, 24 or 32)
529 @return decrypted data (bytes)
530 """
531 output = bytearray(16)
532 # the number of rounds
533 nbrRounds = 0
534 # the 128 bit block to decode
535 block = bytearray(16)
536 # set the number of rounds
537 if size == self.KeySize["SIZE_128"]:
538 nbrRounds = 10
539 elif size == self.KeySize["SIZE_192"]:
540 nbrRounds = 12
541 elif size == self.KeySize["SIZE_256"]:
542 nbrRounds = 14
543 else:
544 raise ValueError("Wrong key size given ({0}).".format(size))
545
546 # the expanded keySize
547 expandedKeySize = 16 * (nbrRounds + 1)
548
549 # Set the block values, for the block:
550 # a0,0 a0,1 a0,2 a0,3
551 # a1,0 a1,1 a1,2 a1,3
552 # a2,0 a2,1 a2,2 a2,3
553 # a3,0 a3,1 a3,2 a3,3
554 # the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3
555
556 # iterate over the columns
557 for i in range(4):
558 # iterate over the rows
559 for j in range(4):
560 block[i + j * 4] = iput[i * 4 + j]
561 # expand the key into an 176, 208, 240 bytes key
562 expandedKey = self.__expandKey(key, size, expandedKeySize)
563 # decrypt the block using the expandedKey
564 block = self.__aes_invMain(block, expandedKey, nbrRounds)
565 # unmap the block again into the output
566 for k in range(4):
567 # iterate over the rows
568 for l in range(4):
569 output[k * 4 + l] = block[k + l * 4]
570 return output
571
572
573 class AESModeOfOperation(object):
574 """
575 Class implementing the different AES mode of operations.
576 """
577 aes = AES()
578
579 # structure of supported modes of operation
580 ModeOfOperation = {
581 "OFB": 0,
582 "CFB": 1,
583 "CBC": 2,
584 }
585
586 def __extractBytes(self, input, start, end, mode):
587 """
588 Private method to extract a range of bytes from the input.
589
590 @param input input data (bytes)
591 @param start start index (integer)
592 @param end end index (integer)
593 @param mode mode of operation (0, 1, 2)
594 @return extracted bytes (bytearray)
595 """
596 if end - start > 16:
597 end = start + 16
598 if mode == self.ModeOfOperation["CBC"]:
599 ar = bytearray(16)
600 else:
601 ar = bytearray()
602
603 i = start
604 j = 0
605 while len(ar) < end - start:
606 ar.append(0)
607 while i < end:
608 ar[j] = input[i]
609 j += 1
610 i += 1
611 return ar
612
613 def encrypt(self, input, mode, key, size, IV):
614 """
615 Public method to perform the encryption operation.
616
617 @param input data to be encrypted (bytes)
618 @param mode mode of operation (0, 1 or 2)
619 @param key key to be used (bytes)
620 @param size length of the key (integer)
621 @param IV initialisation vector (bytearray)
622 @return tuple with mode of operation, length of the input and
623 the encrypted data (integer, integer, bytes)
624 """
625 if len(key) % size:
626 raise ValueError("Illegal size ({0}) for key '{1}'.".format(
627 size, key))
628 if len(IV) % 16:
629 raise ValueError("IV is not a multiple of 16.")
630 # the AES input/output
631 iput = bytearray(16)
632 output = bytearray()
633 ciphertext = bytearray(16)
634 # the output cipher string
635 cipherOut = bytearray()
636 # char firstRound
637 firstRound = True
638 if input:
639 for j in range(int(math.ceil(float(len(input)) / 16))):
640 start = j * 16
641 end = j * 16 + 16
642 if end > len(input):
643 end = len(input)
644 plaintext = self.__extractBytes(input, start, end, mode)
645 # print 'PT@%s:%s' % (j, plaintext)
646 if mode == self.ModeOfOperation["CFB"]:
647 if firstRound:
648 output = self.aes.encrypt(IV, key, size)
649 firstRound = False
650 else:
651 output = self.aes.encrypt(iput, key, size)
652 for i in range(16):
653 if len(plaintext) - 1 < i:
654 ciphertext[i] = 0 ^ output[i]
655 elif len(output) - 1 < i:
656 ciphertext[i] = plaintext[i] ^ 0
657 elif len(plaintext) - 1 < i and len(output) < i:
658 ciphertext[i] = 0 ^ 0
659 else:
660 ciphertext[i] = plaintext[i] ^ output[i]
661 for k in range(end - start):
662 cipherOut.append(ciphertext[k])
663 iput = ciphertext
664 elif mode == self.ModeOfOperation["OFB"]:
665 if firstRound:
666 output = self.aes.encrypt(IV, key, size)
667 firstRound = False
668 else:
669 output = self.aes.encrypt(iput, key, size)
670 for i in range(16):
671 if len(plaintext) - 1 < i:
672 ciphertext[i] = 0 ^ output[i]
673 elif len(output) - 1 < i:
674 ciphertext[i] = plaintext[i] ^ 0
675 elif len(plaintext) - 1 < i and len(output) < i:
676 ciphertext[i] = 0 ^ 0
677 else:
678 ciphertext[i] = plaintext[i] ^ output[i]
679 for k in range(end - start):
680 cipherOut.append(ciphertext[k])
681 iput = output
682 elif mode == self.ModeOfOperation["CBC"]:
683 for i in range(16):
684 if firstRound:
685 iput[i] = plaintext[i] ^ IV[i]
686 else:
687 iput[i] = plaintext[i] ^ ciphertext[i]
688 # print 'IP@%s:%s' % (j, iput)
689 firstRound = False
690 ciphertext = self.aes.encrypt(iput, key, size)
691 # always 16 bytes because of the padding for CBC
692 for k in range(16):
693 cipherOut.append(ciphertext[k])
694 return mode, len(input), bytes(cipherOut)
695
696 # Mode of Operation Decryption
697 # cipherIn - Encrypted String
698 # originalsize - The unencrypted string length - required for CBC
699 # mode - mode of type modeOfOperation
700 # key - a number array of the bit length size
701 # size - the bit length of the key
702 # IV - the 128 bit number array Initilization Vector
703 def decrypt(self, cipherIn, originalsize, mode, key, size, IV):
704 """
705 Public method to perform the decryption operation.
706
707 @param input data to be encrypted (bytes)
708 @param originalsize unencrypted string length (required for CBC)
709 (integer)
710 @param mode mode of operation (0, 1 or 2)
711 @param key key to be used (bytes)
712 @param size length of the key (integer)
713 @param IV initialisation vector (bytearray)
714 @return decrypted data (bytes)
715 """
716 if len(key) % size:
717 raise ValueError("Illegal size ({0}) for key '{1}'.".format(
718 size, key))
719 if len(IV) % 16:
720 raise ValueError("IV is not a multiple of 16.")
721 # the AES input/output
722 ciphertext = bytearray()
723 iput = bytearray()
724 output = bytearray()
725 plaintext = bytearray(16)
726 # the output bytes
727 bytesOut = bytearray()
728 # char firstRound
729 firstRound = True
730 if cipherIn != None:
731 for j in range(int(math.ceil(float(len(cipherIn)) / 16))):
732 start = j * 16
733 end = j * 16 + 16
734 if j * 16 + 16 > len(cipherIn):
735 end = len(cipherIn)
736 ciphertext = cipherIn[start:end]
737 if mode == self.ModeOfOperation["CFB"]:
738 if firstRound:
739 output = self.aes.encrypt(IV, key, size)
740 firstRound = False
741 else:
742 output = self.aes.encrypt(iput, key, size)
743 for i in range(16):
744 if len(output) - 1 < i:
745 plaintext[i] = 0 ^ ciphertext[i]
746 elif len(ciphertext) - 1 < i:
747 plaintext[i] = output[i] ^ 0
748 elif len(output) - 1 < i and len(ciphertext) < i:
749 plaintext[i] = 0 ^ 0
750 else:
751 plaintext[i] = output[i] ^ ciphertext[i]
752 for k in range(end - start):
753 bytesOut.append(plaintext[k])
754 iput = ciphertext
755 elif mode == self.ModeOfOperation["OFB"]:
756 if firstRound:
757 output = self.aes.encrypt(IV, key, size)
758 firstRound = False
759 else:
760 output = self.aes.encrypt(iput, key, size)
761 for i in range(16):
762 if len(output) - 1 < i:
763 plaintext[i] = 0 ^ ciphertext[i]
764 elif len(ciphertext) - 1 < i:
765 plaintext[i] = output[i] ^ 0
766 elif len(output) - 1 < i and len(ciphertext) < i:
767 plaintext[i] = 0 ^ 0
768 else:
769 plaintext[i] = output[i] ^ ciphertext[i]
770 for k in range(end - start):
771 bytesOut.append(plaintext[k])
772 iput = output
773 elif mode == self.ModeOfOperation["CBC"]:
774 output = self.aes.decrypt(ciphertext, key, size)
775 for i in range(16):
776 if firstRound:
777 plaintext[i] = IV[i] ^ output[i]
778 else:
779 plaintext[i] = iput[i] ^ output[i]
780 firstRound = False
781 if originalsize is not None and originalsize < end:
782 for k in range(originalsize - start):
783 bytesOut.append(plaintext[k])
784 else:
785 for k in range(end - start):
786 bytesOut.append(plaintext[k])
787 iput = ciphertext
788 return bytes(bytesOut)
789
790
791 def encryptData(key, data, mode=AESModeOfOperation.ModeOfOperation["CBC"]):
792 """
793 Module function to encrypt the given data with the given key.
794
795 @param key key to be used for encryption (bytes)
796 @param data data to be encrypted (bytes)
797 @param mode mode of operations (0, 1 or 2)
798 @return encrypted data prepended with the initialization vector (bytes)
799 """
800 key = bytearray(key)
801 if mode == AESModeOfOperation.ModeOfOperation["CBC"]:
802 data = append_PKCS7_padding(data)
803 keysize = len(key)
804 assert keysize in AES.KeySize.values(), 'invalid key size: {0}'.format(keysize)
805 # create a new iv using random data
806 iv = bytearray([i for i in os.urandom(16)])
807 moo = AESModeOfOperation()
808 mode, length, ciph = moo.encrypt(data, mode, key, keysize, iv)
809 # With padding, the original length does not need to be known. It's a bad
810 # idea to store the original message length.
811 # prepend the iv.
812 return bytes(iv) + bytes(ciph)
813
814
815 def decryptData(key, data, mode=AESModeOfOperation.ModeOfOperation["CBC"]):
816 """
817 Module function to decrypt the given data with the given key.
818
819 @param key key to be used for decryption (bytes)
820 @param data data to be decrypted (with initialization vector prepended) (bytes)
821 @param mode mode of operations (0, 1 or 2)
822 @return decrypted data (bytes)
823 @exception ValueError key size is invalid or decrypted data is invalid
824 """
825 key = bytearray(key)
826 keysize = len(key)
827 assert keysize in AES.KeySize.values(), 'invalid key size: %s' % keysize
828 # iv is first 16 bytes
829 iv = bytearray(data[:16])
830 data = bytearray(data[16:])
831 moo = AESModeOfOperation()
832 decr = moo.decrypt(data, None, mode, key, keysize, iv)
833 if mode == AESModeOfOperation.ModeOfOperation["CBC"]:
834 decr = strip_PKCS7_padding(decr)
835 return bytes(decr)

eric ide

mercurial