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/
19 '''AES funtions for a single block
21 # Very annoying code: all is for an object, but no state is kept!
22 # Should just be plain functions in a AES modlule.
27 keySize
= dict(SIZE_128
=16, SIZE_192
=24, SIZE_256
=32)
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,
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,
81 def getSBoxValue(self
,num
):
82 """Retrieves a given S-Box Value"""
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).
95 return word
[1:] + word
[:1]
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,
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
133 word
[i
] = self
.getSBoxValue(word
[i
])
134 # XOR the output of the rcon operation with i to the first part
136 word
[0] = word
[0] ^ self
.getRconValue(iteration
)
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
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
]
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
)
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
174 expandedKey
[currentSize
] = expandedKey
[currentSize
- size
] ^ \
180 def addRoundKey(self
, state
, roundKey
):
181 """Adds (XORs) the round key to the state."""
183 state
[i
] ^
= roundKey
[i
]
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.
194 roundKey
[j
*4+i
] = expandedKey
[roundKeyPointer
+ i
*4 + j
]
197 def galois_multiplication(self
, a
, b
):
198 """Galois multiplication of 8 bit characters a and b."""
200 for counter
in range(8):
202 hi_bit_set
= a
& 0x80
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
])
221 # iterate over the 4 rows and call shiftRow() with that row
222 def shiftRows(self
, state
, isInv
):
224 state
= self
.shiftRow(state
, i
*4, i
, isInv
)
227 # each iteration shifts the row to the left by 1
228 def shiftRow(self
, state
, statePointer
, nbr
, isInv
):
231 state
[statePointer
:statePointer
+4] = \
232 state
[statePointer
+3:statePointer
+4] + \
233 state
[statePointer
:statePointer
+3]
235 state
[statePointer
:statePointer
+4] = \
236 state
[statePointer
+1:statePointer
+4] + \
237 state
[statePointer
:statePointer
+1]
240 # galois multiplication of the 4x4 matrix
241 def mixColumns(self
, state
, isInv
):
242 # iterate over the 4 columns
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
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]
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])
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)
284 state
= self
.addRoundKey(state
, roundKey
)
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
)
301 state
= self
.mixColumns(state
, True)
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))
310 state
= self
.aes_round(state
,
311 self
.createRoundKey(expandedKey
, 16*i
), i
)
313 state
= self
.subBytes(state
, False)
314 state
= self
.shiftRows(state
, False)
315 state
= self
.addRoundKey(state
,
316 self
.createRoundKey(expandedKey
, 16*nbrRounds
))
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
))
326 state
= self
.aes_invRound(state
,
327 self
.createRoundKey(expandedKey
, 16*i
), i
)
329 state
= self
.shiftRows(state
, True)
330 state
= self
.subBytes(state
, True)
331 state
= self
.addRoundKey(state
, self
.createRoundKey(expandedKey
, 0))
334 # encrypts a 128 bit input block against the given key of size specified
335 def encrypt(self
, iput
, key
, size
):
337 # the number of rounds
339 # the 128 bit block to encode
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
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
361 # iterate over the rows
363 block
[(i
+(j
*4))] = iput
[(i
*4)+j
]
365 # expand the key into an 176, 208, 240 bytes 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
374 # iterate over the rows
376 output
[(k
*4)+l
] = block
[(k
+(l
*4))]
379 # decrypts a 128 bit input block against the given key of size specified
380 def decrypt(self
, iput
, key
, size
):
382 # the number of rounds
384 # the 128 bit block to decode
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
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
404 # iterate over the rows
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
413 # iterate over the rows
415 output
[(k
*4)+l
] = block
[(k
+(l
*4))]
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.
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
438 while len(ar
) < end
- start
:
441 ar
[j
] = ord(string
[i
])
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
):
457 # the AES input/output
461 ciphertext
= [0] * 16
462 # the output cipher string
467 for j
in range(int(math
.ceil(float(len(stringIn
))/16))):
470 if end
> len(stringIn
):
472 plaintext
= self
.convertString(stringIn
, start
, end
, mode
)
473 # print 'PT@%s:%s' % (j, plaintext)
474 if mode
== self
.modeOfOperation
["CFB"]:
476 output
= self
.aes
.encrypt(IV
, key
, size
)
479 output
= self
.aes
.encrypt(iput
, key
, size
)
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
488 ciphertext
[i
] = plaintext
[i
] ^ output
[i
]
489 for k
in range(end
-start
):
490 cipherOut
.append(ciphertext
[k
])
492 elif mode
== self
.modeOfOperation
["OFB"]:
494 output
= self
.aes
.encrypt(IV
, key
, size
)
497 output
= self
.aes
.encrypt(iput
, key
, size
)
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
506 ciphertext
[i
] = plaintext
[i
] ^ output
[i
]
507 for k
in range(end
-start
):
508 cipherOut
.append(ciphertext
[k
])
510 elif mode
== self
.modeOfOperation
["CBC"]:
513 iput
[i
] = plaintext
[i
] ^ IV
[i
]
515 iput
[i
] = plaintext
[i
] ^ ciphertext
[i
]
516 # print 'IP@%s:%s' % (j, iput)
518 ciphertext
= self
.aes
.encrypt(iput
, key
, size
)
519 # always 16 bytes because of the padding for CBC
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)
537 # the AES input/output
542 # the output plain text character list
547 for j
in range(int(math
.ceil(float(len(cipherIn
))/16))):
550 if j
*16+16 > len(cipherIn
):
552 ciphertext
= cipherIn
[start
:end
]
553 if mode
== self
.modeOfOperation
["CFB"]:
555 output
= self
.aes
.encrypt(IV
, key
, size
)
558 output
= self
.aes
.encrypt(iput
, key
, size
)
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
:
567 plaintext
[i
] = output
[i
] ^ ciphertext
[i
]
568 for k
in range(end
-start
):
569 chrOut
.append(chr(plaintext
[k
]))
571 elif mode
== self
.modeOfOperation
["OFB"]:
573 output
= self
.aes
.encrypt(IV
, key
, size
)
576 output
= self
.aes
.encrypt(iput
, key
, size
)
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
:
585 plaintext
[i
] = output
[i
] ^ ciphertext
[i
]
586 for k
in range(end
-start
):
587 chrOut
.append(chr(plaintext
[k
]))
589 elif mode
== self
.modeOfOperation
["CBC"]:
590 output
= self
.aes
.decrypt(ciphertext
, key
, size
)
593 plaintext
[i
] = IV
[i
] ^ output
[i
]
595 plaintext
[i
] = iput
[i
] ^ output
[i
]
597 if originalsize
is not None and originalsize
< end
:
598 for k
in range(originalsize
-start
):
599 chrOut
.append(chr(plaintext
[k
]))
601 for k
in range(end
-start
):
602 chrOut
.append(chr(plaintext
[k
]))
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
))
619 # raise ValueError("String ending with %r can't be PCKS7-padded" % s[-1])
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
632 if mode
== AESModeOfOperation
.modeOfOperation
["CBC"]:
633 data
= append_PKCS7_padding(data
)
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.
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
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
)
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
)
699 testStr(cleartext
, 16, "CBC")