Cuarte parte de la serie... uffff, esto debía haberse posteado hace un mes xD

¿Qué es AES?
Advanced Encryption Standard (AES), también conocido como Rijndael
(pronunciado "Rain Doll" en inglés), es un esquema de cifrado_por
bloques adoptado como un estándar de cifrado por el gobierno_de_los
Estados_Unidos. Se espera que sea usado en el mundo entero y
analizado exhaustivamente, como fue el caso de su predecesor, el Data
Encryption_Standard (DES). El AES fue anunciado por el Instituto
Nacional_de_Estándares_y_Tecnología (NIST) como FIPS PUB 197 de los
Estados Unidos (FIPS 197) el 26_de_noviembre de 2001 después de un
proceso de estandarización que duró 5 años (véase proceso_de
Advanced_Encryption_Standard para más detalles). Se transformó en
un estándar efectivo el 26_de_mayo de 2002. Desde 2006, el AES es
uno de los algoritmos más populares usados en criptografía
simétrica. [ http://es.wikipedia.org/wiki/
Advanced_Encryption_Standard ]

Así, AES es uno de los algoritmos de cifrado más importantes de la actualidad, por lo que es imposible hablar de criptografía y no hablar de el.

AES es un cifrado por bloques, esto significa que no maneja cada byte independientemente, sino que los agrupa en bloques de 16 bytes, ademas soporta 3 longitudes de clave, con cada uno el proceso de cifrado (las rondas) se repite un numero dado de veces 128 bits - 10 rondas 192 bits - 12 rondas 256 bits - 14 rondas

Nota: Por supuesto, esta implementación (como cualquiera de esta serie) no se debe usar para cosas reales, por que es mas lento que un dialup (está en python para que sea más comprensible, si_quieres_velocidad_busca_algo_en_C, por_ejemplo ) y porque no tiene en cuenta ataques por canal auxiliar [ AES_- Ataques_de_canal_auxiliar ]

Otra cosa, algunas implementaciones representan el "estado" del cifrador como un array de 16 elementos, pero dadas las operaciones, me parecio más gráfico utilizar un array de 4 * 4 (bidimensional)

Antes de entrar en materia, vamos a definir unas cuantas funciones para manejar los datos más cómodamente:

Word to array conversion

def word2array(word):

arr = [0,0,0,0]

arr[3] = word&((1<<8)-1) 

arr[2] = (word/(1<<8))&((1<<8)-1)

arr[1] = (word/(1<<16))&((1<<8)-1)

arr[0] = (word/(1<<24))&((1<<8)-1)

return arr

Array to word conversion

def array2word(arr):

word = 0

arr.reverse()

for i in range(0,len(arr)):

word+=(arr[i]<<(i*8))

return word

String to array conversion

def str2arr(s):

arr = []

for c in s:

arr.append(ord(c))

return arr

Array to string conversion

def arr2str(arr):

s = ""

for c in arr:

s+=chr(c)

return s

Y una función que se encargue de "las matematicas":

Galois Multiplication

def galoisMult(a, b):

p = 0

for counter in range(0,8):

if((b & 1) == 1):

p ^= a

hi_bit_set = (a & 0x80)

a = (a<<1) & 0xFF

if(hi_bit_set == 0x80):

a ^= 0x1b

b >>= 1

return p

Bien, una vez hecho esto, decir que a diferencia de otros cifrados simétricos (como ARC4), no se usa el mismo proceso para cifrar y para descifrar, pero es muy similar, por comodidad empezaremos la parte del cifrado:

Para el cifrado, ademas hay que definir dos estructuras, lo que se conoce como una S-box (caja-S), que se utiliza (veremos después como) en el cifrado y lo que se suele llamar RCon (que en el fondo es una tabla precomputa de las exponenciaciones de 2 en el campo finito de Rinjdael) ,que se utiliza al manejar la clave del cifrado(esto es igual para cifrado y descifrado):

global sbox

global rcon

AES S-Box

sbox = [

0     1    2      3

4    5     6     7      8    9     A      B    C     D     E     F

0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,  #0

0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,  #1

0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,  #2

0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,  #3

0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,  #4

0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,  #5

0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,  #6

0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,  #7

0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,  #8

0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,  #9

0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,  #A

0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,  #B

0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,  #C

0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,  #D

0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,  #E

0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ] #F

AES Rcon

rcon = [

0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,

0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,

0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,

0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d,

0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab,

0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d,

0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25,

0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01,

0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d,

0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa,

0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a,

0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02,

0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,

0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,

0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,

0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,

0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f,

0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5,

0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33,

0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb ]

Ahora veremos las funciones básicas que componen el cifrado... recordemos que el "estado" del cifrador es un array de cuatro por cuatro bytes, y que solo se cifra un bloque (en caso de este algoritmo), de 16 bytes (ya se hablara de como solucionar este problema mas tarde):

La primera funcion se denomina Add Round Key (añadir clave de ronda), y consiste en hacer XOR de el estado del cifrador y de la clave de la ronda (Round Key, de esto se hablara después tambien):

def aes_AddRoundKey(data, key): 

for i in range(0,4):

for j in range(0,4):

data[i][j]^=key[i][j]

return data

Sencillo, no? Seguimos...

La segunda operación se llama Shift Rows (desplazar filas), y consiste en desplazar la primera fila del "estado" del cifrador (la de posición 0), 0 bytes a la izquierda (vamos, no hacer nada), desplazar la segunda (la de la posición 1), 1 byte a la izquerda, y asi hasta la cuarta (posición 3, 3 a la izquierda)

def aes_ShiftRows(data):

new_data=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]

for i in range(0,4):

for j in range (0,4):

new_data[i] [j] = data[i] [ (j+i) % 4]

return new_data

Bien, vamos con la tercera, Sub Bytes (substituir bytes), consiste en substituir cada byte del estado, por el que ocupa el de la S-box en la posicion igual al numero que tiene, es decir: def aes_SubBytes(data):

for i in range(0,4):

for j in range(0,4):

data[i][j]=sbox[data[i][j]]

return data La última de las funciones de cifrado (y me temo que la mas difícil con diferencia) es Mix Columns (mezclar columnas), y esto es basicamente lo que se hace, por comodidad, dividiremos esto en dos partes, la division del "estado" del cifrador en columnas, y su tratamiento, la primera parte es: def aes_MixColumns(data):

for i in range(0,4):

column = []

for j in range(0,4):

column.append(data[j][i])

column = aes_MixColumn(column)

for j in range(0,4):

data[j][i] = column[j]

return data La operación que se aplica sobre cada columna es esta, básicamente lo que hace es hacer el XOR de determinadas multiplicaciones en conjuntos finitos (ehh... da igual, el código esta al principio del post ;)

def aes_MixColumn(data):

tmp = []

for i in data:

tmp.append(i)

for i in range(0,4):

data[i]=galoisMult(tmp[i],2) ^ galoisMult(tmp[ (i+3)%4],1) ^ \

galoisMult(tmp[ (i+2)%4],1) ^ galoisMult(tmp[(i+1)%4],3)

return data Una vez explicadas las funciones básicas de cifrado, se hace necesario hablar de las claves... las claves no se utilizan directamente, primero se expanden, y despúes se obtienen subclaves (las claves de ronda que se mencionaron anteriormente). La expansion sigue el siguiente patrón:

Tamaño de clave expandida = (número de rondas +1) * 16

O... si se prefiere: 128 bits -> 176 bytes 192 bits -> 208 bytes 256 bits -> 240 bytes

Para expandir una clave se utilizan la siguientes funciónes (solo se utilizaria directamente aes_expandKey ):

AES rotate Operation

def aes_rotate(data):

aux=data[0]

for i in range(0,3):

data[i]=data[i+1]

data[3]=aux

return data

AES Key Schedule Core Operation

def aes_KS_core(word,it):

word =  aes_rotate(word)

newWord = []

for i in word:

newWord.append(sbox[i])

newWord[0]^=rcon[it]

return newWord

AES expandKey Operation

def aes_expandKey( key, expandedKeySize):

keyLength=len(key)

rconIteration = 1

tmp = [0,0,0,0]

expandedKey = []

for i in range(0,keyLength):

expandedKey.append(ord(key[i]))

while (len(expandedKey) < expandedKeySize):

for i in range(0,4):

tmp[i] = expandedKey[(len (expandedKey) - 4) + i]

if(len(expandedKey) % keyLength == 0):

tmp=aes_KS_core(tmp, rconIteration)

rconIteration+=1

if(keyLength == 32 and ((len(expandedKey) % keyLength) == 16)):

for i in range(0,4):

tmp[i] = sbox [tmp[i]]

for i in range(0,4):

expandedKey.append(expandedKey [len(expandedKey) - keyLength] ^ tmp[i] )

return expandedKey

Como se puede ver, la función aes_rotate mueve los 4 bytes (un word) hacia la izquierda. aes_KS_core rota el word y compone un nuevo word a partir de el S-box (de forma similar a como lo hace Sub Bytes), por último hace XOR del primer byte y una posicion en el RCon.

Sabiendo esto, se entiende moderadamente bien la función aes_expandKey

Las claves de ronda (Round Keys) son claves de 16 bytes extraidas de la clave expandida, no tiene mucha complicación: def aes_CreateRoundKey(expandedKey,start):

roundKey = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]

for i in range(0,4):

for j in range(0,4):

roundKey[i][j] = expandedKey[ (i*4)+j+start]

return roundKey Ok, ahora hay que juntarlo todo: Si recordais, antes se hablo de rondas... pues bien, en ellas se ejecutan cada una de las funciones de cifrado: def aes_Round(data,key):

data = aes_SubBytes (data)

data = aes_ShiftRows (data)

data = aes_MixColumns (data)

data = aes_AddRoundKey (data,key)

return data Pero se utilizan mas funciones a parte de las rondas la estructura seria mas o menos asi:

def aes_Main(data,expandedKey, nbrRounds):

block = []

for i in range(0,4):

block.append(data[i4:(i+1)4])

roundKey = aes_CreateRoundKey(expandedKey, 0)

block = aes_AddRoundKey(block, roundKey)

for i in range(1,nbrRounds):

roundKey = aes_CreateRoundKey(expandedKey, 16*i)

block = aes_Round(block, roundKey)

roundKey = aes_CreateRoundKey(expandedKey, 16*nbrRounds)

block = aes_SubBytes(block)

block = aes_ShiftRows(block)

block = aes_AddRoundKey(block, roundKey)

data = ""

for b in block:

data+=(arr2str(b))

return data Por ultimo, si añadimos la parte donde se comprueba que la longitud de clave sea correcta y se genera la clave expandida (y de paso añadimos "\0"'s para rellenar los bloques y las contraseñas)...

def aes_Encrypt(data,passwd,size=256):

if (size not in [128,192,256]):

raise Exception('Invalid key length')

while (len(passwd)<(size/8)):

passwd+="\0"

while ((len(data)%0x10)!=0):

data+="\0"

data = str2arr(data)

nbrRounds = {

128: 10,

192: 12,

256: 14

} [size]

expandedKeySize = (nbrRounds+1) * 16

expandedKey = aes_expandKey(passwd, expandedKeySize)

data = aes_Main(data, expandedKey,nbrRounds)

return data El cifrado ya esta :D El descifrado es bastante similar (de hecho al ser igual la parte de las claves, se ahorra mucho codigo :)

Se utiliza una S-box inversa (por decirlo de alguna forma, si teniendo un numero (n) y la posicion n de la S-box es s , la posicion s en la S-box inversa nos devolvera el numero original (n) )

AES RS-Box (reverse S-Box)

rsbox = [

0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,

0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,

0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,

0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,

0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,

0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,

0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,

0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,

0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,

0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,

0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,

0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,

0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,

0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,

0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,

0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ]

Así, la función inversa (de descifrado) de Sub Bytes es igual, pero utilizando la S-box inversa: pre class="brush:python">def aes_invSubBytes(data):

for i in range(0,4):         for j in range(0,4):             data[i][j]=rsbox[data[i][j]]

return data El inverso de Shift Rows consiste en mover las filas igual, pero en sentido contrario: def aes_invShiftRows(data):

new_data=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]

for i in range(0,4):

for j in range(0,4):

new_data[i][j] = data[i] [ (j- i) % 4]

return new_data  La parte de Mix Columns solo cambia en cuanto a las constantes por las que se multiplica:

AES Reverse MixColumns Operation

def aes_invMixColumns(data):

for i in range(0,4):

column = []

for j in range(0,4):

column.append(data[j][i])

column = aes_invMixColumn(column)

for j in range(0,4):

data[j][i] = column[j]

return data

AES Reverse MixColumn Operation

def aes_invMixColumn(data):

tmp = []

for i in data:

tmp.append(i)

for i in range(0,4):

data[i]=galoisMult(tmp[i],14) ^ galoisMult(tmp[ (i+3)%4],9) ^ \

galoisMult(tmp[ (i+2)%4],13) ^ galoisMult(tmp[(i+1)%4],11)

return data Si estas esperando una función inversa a Add Round Key, que sepas que no la hay, está basada en XOR, asi que no necesita una version inversa Pero si que existe una ronda inversa (que consiste en utilizar el inverso de las operaciones, pero tambien en un orden distinto): def aes_invRound(data,key):

data = aes_invShiftRows (data)

data = aes_invSubBytes (data)

data = aes_AddRoundKey (data,key)

data = aes_invMixColumns (data)

return data Por supuesto, las rondas no van en el mismo orden (en vez de ir de 0 al maximo de rondas, van en "direccion contraria", decreciendo), y tambien hay otros pequeños cambios de orden: def aes_invMain(data,expandedKey, nbrRounds):

block = []

for i in range(0,4):

block.append(data[i4:(i+1)4])

roundKey = aes_CreateRoundKey(expandedKey, 16*nbrRounds)

block = aes_AddRoundKey(block, roundKey)

for i in range(nbrRounds-1,0,-1):

roundKey = aes_CreateRoundKey(expandedKey, 16*i)

block = aes_invRound(block, roundKey)

roundKey = aes_CreateRoundKey(expandedKey, 0)

block = aes_invShiftRows(block)

block = aes_invSubBytes(block)

block = aes_AddRoundKey(block, roundKey)

data = ""

for b in block:

data+=(arr2str(b))

return data Por ultimo, queda hacer la función que se encarga de todo el proceso de expansion de clave y descifrado (la diferencia con la de cifrado se reduce a ejecutar la funcion principal inversa, en vez de la normal): def aes_Decrypt(data,passwd,size=256):

if (size not in [128,192,256]):

raise Exception('Invalid key length')

while (len(passwd)<(size/8)):

passwd+="\0"

while ((len(data)&0xFF)<0xF):

data+="\0"

data = str2arr(data)

nbrRounds = {

128: 10,

192: 12,

256: 14

} [size]

expandedKeySize = (nbrRounds+1) * 16

expandedKey = aes_expandKey(passwd, expandedKeySize)

data = aes_invMain(data, expandedKey,nbrRounds)

return data Y ya esta listo :D, podemos cifrar cualquier cosa de 16 bytes... pero eso seguramente se quede corto, no pretendo hablar aqui de modos de operacion de cifrado de bloques (como solucionar esta problema), pero dire donde podeis encontrar mas informacion:

http://www.progressive-coding.com/tutorial.php?id=3 (en inglés) http://es.wikipedia.org/wiki/ Modos_de_operaci%C3%B3n_de_una_unidad_de_cifrado_por_bloques Como ejemplo muuuuy sencillo (y para nada seguro), se podrian escribir estas funciones:

AES sample multi-byte encryption

def aes_Long_Encrypt(text,key):

out=""

while (len(text)>15):

out+=aes_Encrypt(text[0:16],key)

text=text[16:]

if (len(text)>0):

out+=aes_Encrypt(text,key)

return out

AES sample multi-byte decryption

def aes_Long_Decrypt(text,key):

out=""

while (len(text)>15):

out+=aes_Decrypt(text[0:16],key)

text=text[16:]

if (len(text)>0):

out+=aes_Decrypt(text,key)

return out Aqui teneis el codigo fuente completo [ aes.py ]

Y eso es todo! [Referencias] (casi todo en inglés) http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf http://brandon.sternefamily.net/posts/2007/06/aes-tutorial-python- implementation/ http://www.moserware.com/2009/09/stick-figure-guide-to-advanced.html http://www.progressive-coding.com/tutorial.php http://es.wikipedia.org/wiki/Advanced_Encryption_Standard