src/eric7/Utilities/crypto/py3AES.py

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

eric ide

mercurial