miércoles, 26 de mayo de 2010

Introduccion a la criptografia, con Python: AES (IV)

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[i*4:(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:

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 cosiste 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[i*4:(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

No hay comentarios:

Publicar un comentario