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 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> 21# 22 23""" 24Module implementing classes for encryption according 25Advanced Encryption Standard. 26""" 27 28import os 29import math 30 31 32def 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 43def 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 61class 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 606class 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 822def 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 848def 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) 871