modified: nfig1.py
[GalaxyCodeBases.git] / etc / Windows / py-kms / aes.py
blob90aee895a9c15d490da34f76475f847a174fdd12
1 #!/usr/bin/python
3 # aes.py: implements AES - Advanced Encryption Standard
4 # from the SlowAES project, http://code.google.com/p/slowaes/
6 # Copyright (c) 2008 Josh Davis ( http://www.josh-davis.org ),
7 # Alex Martelli ( http://www.aleax.it )
9 # Ported from C code written by Laurent Haan ( http://www.progressive-coding.com )
11 # Licensed under the Apache License, Version 2.0
12 # http://www.apache.org/licenses/
14 import os
15 import sys
16 import math
18 class AES(object):
19 '''AES funtions for a single block
20 '''
21 # Very annoying code: all is for an object, but no state is kept!
22 # Should just be plain functions in a AES modlule.
24 v6 = False
26 # valid key sizes
27 keySize = dict(SIZE_128=16, SIZE_192=24, SIZE_256=32)
29 # Rijndael S-box
30 sbox = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67,
31 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59,
32 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7,
33 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1,
34 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05,
35 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83,
36 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29,
37 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
38 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa,
39 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c,
40 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc,
41 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
42 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19,
43 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee,
44 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49,
45 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
46 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4,
47 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6,
48 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70,
49 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9,
50 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e,
51 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1,
52 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0,
53 0x54, 0xbb, 0x16]
55 # Rijndael Inverted S-box
56 rsbox = [0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3,
57 0x9e, 0x81, 0xf3, 0xd7, 0xfb , 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f,
58 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb , 0x54,
59 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b,
60 0x42, 0xfa, 0xc3, 0x4e , 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24,
61 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25 , 0x72, 0xf8,
62 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d,
63 0x65, 0xb6, 0x92 , 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
64 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84 , 0x90, 0xd8, 0xab,
65 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3,
66 0x45, 0x06 , 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1,
67 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b , 0x3a, 0x91, 0x11, 0x41,
68 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6,
69 0x73 , 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9,
70 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e , 0x47, 0xf1, 0x1a, 0x71, 0x1d,
71 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b ,
72 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0,
73 0xfe, 0x78, 0xcd, 0x5a, 0xf4 , 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07,
74 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f , 0x60,
75 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f,
76 0x93, 0xc9, 0x9c, 0xef , 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5,
77 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61 , 0x17, 0x2b,
78 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55,
79 0x21, 0x0c, 0x7d]
81 def getSBoxValue(self,num):
82 """Retrieves a given S-Box Value"""
83 return self.sbox[num]
85 def getSBoxInvert(self,num):
86 """Retrieves a given Inverted S-Box Value"""
87 return self.rsbox[num]
89 def rotate(self, word):
90 """ Rijndael's key schedule rotate operation.
92 Rotate a word eight bits to the left: eg, rotate(1d2c3a4f) == 2c3a4f1d
93 Word is an char list of size 4 (32 bits overall).
94 """
95 return word[1:] + word[:1]
97 # Rijndael Rcon
98 Rcon = [0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
99 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97,
100 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72,
101 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66,
102 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
103 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d,
104 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
105 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61,
106 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
107 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40,
108 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc,
109 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5,
110 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a,
111 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d,
112 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
113 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
114 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4,
115 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
116 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08,
117 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
118 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d,
119 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2,
120 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74,
121 0xe8, 0xcb ]
123 def getRconValue(self, num):
124 """Retrieves a given Rcon Value"""
125 return self.Rcon[num]
127 def core(self, word, iteration):
128 """Key schedule core."""
129 # rotate the 32-bit word 8 bits to the left
130 word = self.rotate(word)
131 # apply S-Box substitution on all 4 parts of the 32-bit word
132 for i in range(4):
133 word[i] = self.getSBoxValue(word[i])
134 # XOR the output of the rcon operation with i to the first part
135 # (leftmost) only
136 word[0] = word[0] ^ self.getRconValue(iteration)
137 return word
139 def expandKey(self, key, size, expandedKeySize):
140 """Rijndael's key expansion.
142 Expands an 128,192,256 key into an 176,208,240 bytes key
144 expandedKey is a char list of large enough size,
145 key is the non-expanded key.
147 # current expanded keySize, in bytes
148 currentSize = 0
149 rconIteration = 1
150 expandedKey = [0] * expandedKeySize
152 # set the 16, 24, 32 bytes of the expanded key to the input key
153 for j in range(size):
154 expandedKey[j] = key[j]
155 currentSize += size
157 while currentSize < expandedKeySize:
158 # assign the previous 4 bytes to the temporary value t
159 t = expandedKey[currentSize-4:currentSize]
161 # every 16,24,32 bytes we apply the core schedule to t
162 # and increment rconIteration afterwards
163 if currentSize % size == 0:
164 t = self.core(t, rconIteration)
165 rconIteration += 1
166 # For 256-bit keys, we add an extra sbox to the calculation
167 if size == self.keySize["SIZE_256"] and ((currentSize % size) == 16):
168 for l in range(4): t[l] = self.getSBoxValue(t[l])
170 # We XOR t with the four-byte block 16,24,32 bytes before the new
171 # expanded key. This becomes the next four bytes in the expanded
172 # key.
173 for m in range(4):
174 expandedKey[currentSize] = expandedKey[currentSize - size] ^ \
175 t[m]
176 currentSize += 1
178 return expandedKey
180 def addRoundKey(self, state, roundKey):
181 """Adds (XORs) the round key to the state."""
182 for i in range(16):
183 state[i] ^= roundKey[i]
184 return state
186 def createRoundKey(self, expandedKey, roundKeyPointer):
187 """Create a round key.
188 Creates a round key from the given expanded key and the
189 position within the expanded key.
191 roundKey = [0] * 16
192 for i in range(4):
193 for j in range(4):
194 roundKey[j*4+i] = expandedKey[roundKeyPointer + i*4 + j]
195 return roundKey
197 def galois_multiplication(self, a, b):
198 """Galois multiplication of 8 bit characters a and b."""
199 p = 0
200 for counter in range(8):
201 if b & 1: p ^= a
202 hi_bit_set = a & 0x80
203 a <<= 1
204 # keep a 8 bit
205 a &= 0xFF
206 if hi_bit_set:
207 a ^= 0x1b
208 b >>= 1
209 return p
212 # substitute all the values from the state with the value in the SBox
213 # using the state value as index for the SBox
215 def subBytes(self, state, isInv):
216 if isInv: getter = self.getSBoxInvert
217 else: getter = self.getSBoxValue
218 for i in range(16): state[i] = getter(state[i])
219 return state
221 # iterate over the 4 rows and call shiftRow() with that row
222 def shiftRows(self, state, isInv):
223 for i in range(4):
224 state = self.shiftRow(state, i*4, i, isInv)
225 return state
227 # each iteration shifts the row to the left by 1
228 def shiftRow(self, state, statePointer, nbr, isInv):
229 for i in range(nbr):
230 if isInv:
231 state[statePointer:statePointer+4] = \
232 state[statePointer+3:statePointer+4] + \
233 state[statePointer:statePointer+3]
234 else:
235 state[statePointer:statePointer+4] = \
236 state[statePointer+1:statePointer+4] + \
237 state[statePointer:statePointer+1]
238 return state
240 # galois multiplication of the 4x4 matrix
241 def mixColumns(self, state, isInv):
242 # iterate over the 4 columns
243 for i in range(4):
244 # construct one column by slicing over the 4 rows
245 column = state[i:i+16:4]
246 # apply the mixColumn on one column
247 column = self.mixColumn(column, isInv)
248 # put the values back into the state
249 state[i:i+16:4] = column
251 return state
253 # galois multiplication of 1 column of the 4x4 matrix
254 def mixColumn(self, column, isInv):
255 if isInv: mult = [14, 9, 13, 11]
256 else: mult = [2, 1, 1, 3]
257 cpy = list(column)
258 g = self.galois_multiplication
260 column[0] = g(cpy[0], mult[0]) ^ g(cpy[3], mult[1]) ^ \
261 g(cpy[2], mult[2]) ^ g(cpy[1], mult[3])
262 column[1] = g(cpy[1], mult[0]) ^ g(cpy[0], mult[1]) ^ \
263 g(cpy[3], mult[2]) ^ g(cpy[2], mult[3])
264 column[2] = g(cpy[2], mult[0]) ^ g(cpy[1], mult[1]) ^ \
265 g(cpy[0], mult[2]) ^ g(cpy[3], mult[3])
266 column[3] = g(cpy[3], mult[0]) ^ g(cpy[2], mult[1]) ^ \
267 g(cpy[1], mult[2]) ^ g(cpy[0], mult[3])
268 return column
270 # applies the 4 operations of the forward round in sequence
271 def aes_round(self, state, roundKey, round):
272 state = self.subBytes(state, False)
273 state = self.shiftRows(state, False)
274 state = self.mixColumns(state, False)
276 if self.v6:
277 if round == 4:
278 state[0]^=0x73
279 if round == 6:
280 state[0]^=0x09
281 if round == 8:
282 state[0]^=0xE4
284 state = self.addRoundKey(state, roundKey)
285 return state
287 # applies the 4 operations of the inverse round in sequence
288 def aes_invRound(self, state, roundKey, round):
289 state = self.shiftRows(state, True)
290 state = self.subBytes(state, True)
291 state = self.addRoundKey(state, roundKey)
293 if self.v6:
294 if round == 4:
295 state[0]^=0x73
296 if round == 6:
297 state[0]^=0x09
298 if round == 8:
299 state[0]^=0xE4
301 state = self.mixColumns(state, True)
302 return state
304 # Perform the initial operations, the standard round, and the final
305 # operations of the forward aes, creating a round key for each round
306 def aes_main(self, state, expandedKey, nbrRounds):
307 state = self.addRoundKey(state, self.createRoundKey(expandedKey, 0))
308 i = 1
309 while i < nbrRounds:
310 state = self.aes_round(state,
311 self.createRoundKey(expandedKey, 16*i), i)
312 i += 1
313 state = self.subBytes(state, False)
314 state = self.shiftRows(state, False)
315 state = self.addRoundKey(state,
316 self.createRoundKey(expandedKey, 16*nbrRounds))
317 return state
319 # Perform the initial operations, the standard round, and the final
320 # operations of the inverse aes, creating a round key for each round
321 def aes_invMain(self, state, expandedKey, nbrRounds):
322 state = self.addRoundKey(state,
323 self.createRoundKey(expandedKey, 16*nbrRounds))
324 i = nbrRounds - 1
325 while i > 0:
326 state = self.aes_invRound(state,
327 self.createRoundKey(expandedKey, 16*i), i)
328 i -= 1
329 state = self.shiftRows(state, True)
330 state = self.subBytes(state, True)
331 state = self.addRoundKey(state, self.createRoundKey(expandedKey, 0))
332 return state
334 # encrypts a 128 bit input block against the given key of size specified
335 def encrypt(self, iput, key, size):
336 output = [0] * 16
337 # the number of rounds
338 nbrRounds = 0
339 # the 128 bit block to encode
340 block = [0] * 16
341 # set the number of rounds
342 if size == self.keySize["SIZE_128"]: nbrRounds = 10
343 elif size == self.keySize["SIZE_192"]: nbrRounds = 12
344 elif size == self.keySize["SIZE_256"]: nbrRounds = 14
345 # The KMS v4 parameters
346 elif size == 20: nbrRounds = 11
347 else: return None
349 # the expanded keySize
350 expandedKeySize = 16*(nbrRounds+1)
352 # Set the block values, for the block:
353 # a0,0 a0,1 a0,2 a0,3
354 # a1,0 a1,1 a1,2 a1,3
355 # a2,0 a2,1 a2,2 a2,3
356 # a3,0 a3,1 a3,2 a3,3
357 # the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3
359 # iterate over the columns
360 for i in range(4):
361 # iterate over the rows
362 for j in range(4):
363 block[(i+(j*4))] = iput[(i*4)+j]
365 # expand the key into an 176, 208, 240 bytes key
366 # the expanded key
367 expandedKey = self.expandKey(key, size, expandedKeySize)
369 # encrypt the block using the expandedKey
370 block = self.aes_main(block, expandedKey, nbrRounds)
372 # unmap the block again into the output
373 for k in range(4):
374 # iterate over the rows
375 for l in range(4):
376 output[(k*4)+l] = block[(k+(l*4))]
377 return output
379 # decrypts a 128 bit input block against the given key of size specified
380 def decrypt(self, iput, key, size):
381 output = [0] * 16
382 # the number of rounds
383 nbrRounds = 0
384 # the 128 bit block to decode
385 block = [0] * 16
386 # set the number of rounds
387 if size == self.keySize["SIZE_128"]: nbrRounds = 10
388 elif size == self.keySize["SIZE_192"]: nbrRounds = 12
389 elif size == self.keySize["SIZE_256"]: nbrRounds = 14
390 else: return None
392 # the expanded keySize
393 expandedKeySize = 16*(nbrRounds+1)
395 # Set the block values, for the block:
396 # a0,0 a0,1 a0,2 a0,3
397 # a1,0 a1,1 a1,2 a1,3
398 # a2,0 a2,1 a2,2 a2,3
399 # a3,0 a3,1 a3,2 a3,3
400 # the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3
402 # iterate over the columns
403 for i in range(4):
404 # iterate over the rows
405 for j in range(4):
406 block[(i+(j*4))] = iput[(i*4)+j]
407 # expand the key into an 176, 208, 240 bytes key
408 expandedKey = self.expandKey(key, size, expandedKeySize)
409 # decrypt the block using the expandedKey
410 block = self.aes_invMain(block, expandedKey, nbrRounds)
411 # unmap the block again into the output
412 for k in range(4):
413 # iterate over the rows
414 for l in range(4):
415 output[(k*4)+l] = block[(k+(l*4))]
416 return output
419 class AESModeOfOperation(object):
420 '''Handles AES with plaintext consistingof multiple blocks.
421 Choice of block encoding modes: OFT, CFB, CBC
423 # Very annoying code: all is for an object, but no state is kept!
424 # Should just be plain functions in an AES_BlockMode module.
425 aes = AES()
427 # structure of supported modes of operation
428 modeOfOperation = dict(OFB=0, CFB=1, CBC=2)
430 # converts a 16 character string into a number array
431 def convertString(self, string, start, end, mode):
432 if end - start > 16: end = start + 16
433 if mode == self.modeOfOperation["CBC"]: ar = [0] * 16
434 else: ar = []
436 i = start
437 j = 0
438 while len(ar) < end - start:
439 ar.append(0)
440 while i < end:
441 ar[j] = ord(string[i])
442 j += 1
443 i += 1
444 return ar
446 # Mode of Operation Encryption
447 # stringIn - Input String
448 # mode - mode of type modeOfOperation
449 # hexKey - a hex key of the bit length size
450 # size - the bit length of the key
451 # hexIV - the 128 bit hex Initilization Vector
452 def encrypt(self, stringIn, mode, key, size, IV):
453 if len(key) % size:
454 return None
455 if len(IV) % 16:
456 return None
457 # the AES input/output
458 plaintext = []
459 iput = [0] * 16
460 output = []
461 ciphertext = [0] * 16
462 # the output cipher string
463 cipherOut = []
464 # char firstRound
465 firstRound = True
466 if stringIn != None:
467 for j in range(int(math.ceil(float(len(stringIn))/16))):
468 start = j*16
469 end = j*16+16
470 if end > len(stringIn):
471 end = len(stringIn)
472 plaintext = self.convertString(stringIn, start, end, mode)
473 # print 'PT@%s:%s' % (j, plaintext)
474 if mode == self.modeOfOperation["CFB"]:
475 if firstRound:
476 output = self.aes.encrypt(IV, key, size)
477 firstRound = False
478 else:
479 output = self.aes.encrypt(iput, key, size)
480 for i in range(16):
481 if len(plaintext)-1 < i:
482 ciphertext[i] = 0 ^ output[i]
483 elif len(output)-1 < i:
484 ciphertext[i] = plaintext[i] ^ 0
485 elif len(plaintext)-1 < i and len(output) < i:
486 ciphertext[i] = 0 ^ 0
487 else:
488 ciphertext[i] = plaintext[i] ^ output[i]
489 for k in range(end-start):
490 cipherOut.append(ciphertext[k])
491 iput = ciphertext
492 elif mode == self.modeOfOperation["OFB"]:
493 if firstRound:
494 output = self.aes.encrypt(IV, key, size)
495 firstRound = False
496 else:
497 output = self.aes.encrypt(iput, key, size)
498 for i in range(16):
499 if len(plaintext)-1 < i:
500 ciphertext[i] = 0 ^ output[i]
501 elif len(output)-1 < i:
502 ciphertext[i] = plaintext[i] ^ 0
503 elif len(plaintext)-1 < i and len(output) < i:
504 ciphertext[i] = 0 ^ 0
505 else:
506 ciphertext[i] = plaintext[i] ^ output[i]
507 for k in range(end-start):
508 cipherOut.append(ciphertext[k])
509 iput = output
510 elif mode == self.modeOfOperation["CBC"]:
511 for i in range(16):
512 if firstRound:
513 iput[i] = plaintext[i] ^ IV[i]
514 else:
515 iput[i] = plaintext[i] ^ ciphertext[i]
516 # print 'IP@%s:%s' % (j, iput)
517 firstRound = False
518 ciphertext = self.aes.encrypt(iput, key, size)
519 # always 16 bytes because of the padding for CBC
520 for k in range(16):
521 cipherOut.append(ciphertext[k])
522 return mode, len(stringIn), cipherOut
524 # Mode of Operation Decryption
525 # cipherIn - Encrypted String
526 # originalsize - The unencrypted string length - required for CBC
527 # mode - mode of type modeOfOperation
528 # key - a number array of the bit length size
529 # size - the bit length of the key
530 # IV - the 128 bit number array Initilization Vector
531 def decrypt(self, cipherIn, originalsize, mode, key, size, IV):
532 # cipherIn = unescCtrlChars(cipherIn)
533 if len(key) % size:
534 return None
535 if len(IV) % 16:
536 return None
537 # the AES input/output
538 ciphertext = []
539 iput = []
540 output = []
541 plaintext = [0] * 16
542 # the output plain text character list
543 chrOut = []
544 # char firstRound
545 firstRound = True
546 if cipherIn != None:
547 for j in range(int(math.ceil(float(len(cipherIn))/16))):
548 start = j*16
549 end = j*16+16
550 if j*16+16 > len(cipherIn):
551 end = len(cipherIn)
552 ciphertext = cipherIn[start:end]
553 if mode == self.modeOfOperation["CFB"]:
554 if firstRound:
555 output = self.aes.encrypt(IV, key, size)
556 firstRound = False
557 else:
558 output = self.aes.encrypt(iput, key, size)
559 for i in range(16):
560 if len(output)-1 < i:
561 plaintext[i] = 0 ^ ciphertext[i]
562 elif len(ciphertext)-1 < i:
563 plaintext[i] = output[i] ^ 0
564 elif len(output)-1 < i and len(ciphertext) < i:
565 plaintext[i] = 0 ^ 0
566 else:
567 plaintext[i] = output[i] ^ ciphertext[i]
568 for k in range(end-start):
569 chrOut.append(chr(plaintext[k]))
570 iput = ciphertext
571 elif mode == self.modeOfOperation["OFB"]:
572 if firstRound:
573 output = self.aes.encrypt(IV, key, size)
574 firstRound = False
575 else:
576 output = self.aes.encrypt(iput, key, size)
577 for i in range(16):
578 if len(output)-1 < i:
579 plaintext[i] = 0 ^ ciphertext[i]
580 elif len(ciphertext)-1 < i:
581 plaintext[i] = output[i] ^ 0
582 elif len(output)-1 < i and len(ciphertext) < i:
583 plaintext[i] = 0 ^ 0
584 else:
585 plaintext[i] = output[i] ^ ciphertext[i]
586 for k in range(end-start):
587 chrOut.append(chr(plaintext[k]))
588 iput = output
589 elif mode == self.modeOfOperation["CBC"]:
590 output = self.aes.decrypt(ciphertext, key, size)
591 for i in range(16):
592 if firstRound:
593 plaintext[i] = IV[i] ^ output[i]
594 else:
595 plaintext[i] = iput[i] ^ output[i]
596 firstRound = False
597 if originalsize is not None and originalsize < end:
598 for k in range(originalsize-start):
599 chrOut.append(chr(plaintext[k]))
600 else:
601 for k in range(end-start):
602 chrOut.append(chr(plaintext[k]))
603 iput = ciphertext
604 return "".join(chrOut)
607 def append_PKCS7_padding(s):
608 """return s padded to a multiple of 16-bytes by PKCS7 padding"""
609 numpads = 16 - (len(s)%16)
610 return s + numpads*chr(numpads)
612 def strip_PKCS7_padding(s):
613 """return s stripped of PKCS7 padding"""
614 if len(s)%16 or not s:
615 raise ValueError("String of len %d can't be PCKS7-padded" % len(s))
616 numpads = ord(s[-1])
617 if numpads > 16:
618 return s
619 # raise ValueError("String ending with %r can't be PCKS7-padded" % s[-1])
620 return s[:-numpads]
622 def encryptData(key, data, mode=AESModeOfOperation.modeOfOperation["CBC"]):
623 """encrypt `data` using `key`
625 `key` should be a string of bytes.
627 returned cipher is a string of bytes prepended with the initialization
628 vector.
631 key = map(ord, key)
632 if mode == AESModeOfOperation.modeOfOperation["CBC"]:
633 data = append_PKCS7_padding(data)
634 keysize = len(key)
635 assert keysize in AES.keySize.values(), 'invalid key size: %s' % keysize
636 # create a new iv using random data
637 iv = [ord(i) for i in os.urandom(16)]
638 moo = AESModeOfOperation()
639 (mode, length, ciph) = moo.encrypt(data, mode, key, keysize, iv)
640 # With padding, the original length does not need to be known. It's a bad
641 # idea to store the original message length.
642 # prepend the iv.
643 return ''.join(map(chr, iv)) + ''.join(map(chr, ciph))
645 def decryptData(key, data, mode=AESModeOfOperation.modeOfOperation["CBC"]):
646 """decrypt `data` using `key`
648 `key` should be a string of bytes.
650 `data` should have the initialization vector prepended as a string of
651 ordinal values.
654 key = map(ord, key)
655 keysize = len(key)
656 assert keysize in AES.keySize.values(), 'invalid key size: %s' % keysize
657 # iv is first 16 bytes
658 iv = map(ord, data[:16])
659 data = map(ord, data[16:])
660 moo = AESModeOfOperation()
661 decr = moo.decrypt(data, None, mode, key, keysize, iv)
662 if mode == AESModeOfOperation.modeOfOperation["CBC"]:
663 decr = strip_PKCS7_padding(decr)
664 return decr
666 def generateRandomKey(keysize):
667 """Generates a key from random data of length `keysize`.
668 The returned key is a string of bytes.
670 if keysize not in (16, 24, 32):
671 emsg = 'Invalid keysize, %s. Should be one of (16, 24, 32).'
672 raise ValueError, emsg % keysize
673 return os.urandom(keysize)
675 def testStr(cleartext, keysize=16, modeName = "CBC"):
676 '''Test with random key, choice of mode.'''
677 print 'Random key test', 'Mode:', modeName
678 print 'cleartext:', cleartext
679 key = generateRandomKey(keysize)
680 print 'Key:', [ord(x) for x in key]
681 mode = AESModeOfOperation.modeOfOperation[modeName]
682 cipher = encryptData(key, cleartext, mode)
683 print 'Cipher:', [ord(x) for x in cipher]
684 decr = decryptData(key, cipher, mode)
685 print 'Decrypted:', decr
688 if __name__ == "__main__":
689 moo = AESModeOfOperation()
690 cleartext = "This is a test with several blocks!"
691 cypherkey = [143,194,34,208,145,203,230,143,177,246,97,206,145,92,255,84]
692 iv = [103,35,148,239,76,213,47,118,255,222,123,176,106,134,98,92]
693 mode, orig_len, ciph = moo.encrypt(cleartext, moo.modeOfOperation["CBC"],
694 cypherkey, moo.aes.keySize["SIZE_128"], iv)
695 print 'm=%s, ol=%s (%s), ciph=%s' % (mode, orig_len, len(cleartext), ciph)
696 decr = moo.decrypt(ciph, orig_len, mode, cypherkey,
697 moo.aes.keySize["SIZE_128"], iv)
698 print decr
699 testStr(cleartext, 16, "CBC")