AES algorithm · Criptografía

Massin Laa.
8 min readApr 4, 2024

--

Advanced Encryption Standard

Cuando el algoritmo de DES era inseguro el NIST (National Institute of Standards and Technology), lanzo un concurso para sustituirlo y apareció el AES.

A día de hoy esta a prueba de la computación cuántica en 256bits.

Tipo de criptosistema:  Criptosistema de bloque y simétrico
Mida de bloque: 128 bits fijos
Mida de llave k: 128, 192 y 256 bits

Numero iteraciones: 10 vueltas/iteraciones si la mida de llave k = 128 bits (estandard)
12 vueltas/iteraciones si la mida de llave k = 192 bits
14 vueltas/iteraciones si la mida de llave k = 256 bits

Como funciona el AES?

AES trabaja con tamaños de bloque de 128bits y llaves de diferentes tamaños (128b, 192b o 256b), este tamaño de bloque fijo se estableció así por varias razones; como la eficiencia, seguridad, interoperabilidad y estandarización.
Mientras que el algoritmo de Rijndael, el cual es la base del AES, puede operar con diferentes tamaños de bloque y llaves.
En la actualidad se utiliza el AES con tamaño de bloque fijo 128b y llaves de 256b.

Recordemos que el AES es un algoritmo de cifrado simétrico con lo que el tanto el emisor y receptor han de tener la misma llave para cifrar y/o descifrar el mensaje.

Se utiliza la misma llave para cifrar y descifrar el mensaje.

De seguro te estarás preguntando, y como viaja la llave de forma segura?
Bien, AES en sí, no es proporciona un método para compartir llaves de forma segura, ya que AES es un algoritmo de cifrado simétrico y no está involucrado en el intercambio de claves.
Pero si se pueden utilizar otros métodos para compartir la llave del AES de manera segura, y uno de los métodos comunes es el intercambio de claves usando criptografía asimétrica (como el RSA).

Pseudocodigo en Java, del algoritmo AES (versión de 128 bits):

public class AES {
private static final int Nb = 4;
private static final int Nr = 10; // Dependiendo de la longitud de la llave k, en este caso ponemos de ejemplo 128b

public static void cipherAES(byte[] in, byte[] out, byte[][] w) {
byte[][] state = new byte[4][Nb];

for (int i = 0; i < 4; i++) {
for (int j = 0; j < Nb; j++) {
state[i][j] = in[i * Nb + j];
}
}

// (0) Initial round:
addRoundKey(state, w, 0);

// (1) Main rounds:
for (int round = 1; round < Nr; round++) {
subBytes(state);
shiftRows(state);
mixColumns(state);
addRoundKey(state, w, round * Nb);
}

// Final round:
subBytes(state);
shiftRows(state);
addRoundKey(state, w, Nr * Nb);

for (int i = 0; i < 4; i++) {
for (int j = 0; j < Nb; j++) {
out[i * Nb + j] = state[i][j];
}
}
}

(0) Initial Round: XOR entre la tabla STATE y la llave k

(1) Main Rounds: Iterar sobre los procedimientos de encriptación.

(2) Final Round: Iteración final.

Ejemplo: Para encriptar con AES el siguiente mensaje m de un tamaño de 128b, utilizando una llave k de 128b:

  • m en hexa (128b) = 32 43 f6 a8 88 5a 30 8d 31 31 98 a2 e0 37 07 34
  • k en hexa (128b) = 2b 7e 15 16 28 ae d2 a6 ab f7 15 88 09 cf 4f 3c

*Los espacios solo sirven de forma visual, no se tiene en cuenta en ningún proceso.

(0) Initial Round

Representamos en una matriz de 4fil x 4col el bloque de mensaje m,
y otra matriz de 4fil x (num_Bytes)col para este caso que tenemos una llave k de 128b sera de 4fil x 4col (en el caso que tuviésemos una llave k de 192b num_Bytes=6col o una llave k de 256b entonces num_Bytes=8col):

*Fijate que ponemos el mensaje m y la llave k, de forma vertical!

La matriz donde ponemos la información a cifrar recibe el nombre de state. (cada casilla solo puede contener 8bits)

(0.1) XOR

Vamos ha hacer una XOR entre la matriz del mensaje m y la matriz de la llave k; STATE XOR CIPHERKEY

*No hay carry al hacer el XOR

(1) Main Rounds

En este paso hemos de iterar sobre los diferentes procesos de encriptación, pero primero hemos de ver cuantas iteraciones hemos de hacer, para ello nos fijamos en la siguiente tabla.
En este ejemplo recuerda que tenemos 128bits de bloque para el mensaje y 128b de la llave k, entonces haremos 10 iteraciones:

Una vez sabemos cuantas iteraciones hemos de hacer sobre los procesos de encriptación, vamos a realizarlas, con el siguiente orden:

  1. SubBytes(state)
  2. ShiftRows(state)
  3. MixColumns(state)
  4. AddRoundKey(state, w, round * Nb)
Actualmente nos encontramos en el punto amarillo, vemos que en una iteración hacemos los procesos de encriptación.

(0.1) Procesos de encriptación

Ejecutamos los siguientes procesos de encriptación, en orden:

1 SubBytes(state): Esta operación sustituimos cada uno de los bytes en el state por valores de una tabla llamada Rijndael S-BOX

S-BOX del AES, que utiliza en el proceso; SubBytes

Vemos en este sencillo GIF, si por ejemplo, en la primera casilla (primer byte 19) que:
- El primer dígito de 19 es 1, lo usamos para buscar en el FILA 1.
- El segundo es 9, lo usamos para buscar en el COLUMNA 9.
- La celda en la que coincidimos es el valor que buscamos [1][9] = d4

GIF — SubBytes

Tenemos como resultado la siguiente tabla STATE, la cual la usaremos para el siguiente proceso:

tabla resultante

2 ShiftRows(state): En este proceso alteramos las filas de la tabla STATE que hemos obtenido en el proceso SubByteRow(state) moviendo las filas n posiciones.

En la siguiente tabla resumo el número de desplazamientos laterales hacia la izquierda (siendo fila 0, la primera fila),
en nuestro caso como tenemos un tamaño de bloque fijo, es decir 128b, entonces haremos 0, 1, 2, 3 desplazamientos laterales en cada fila:

En resumen, para nuestro caso:

  • (fila 0) La primera línea se queda igual.
  • (fila 1) En la segunda línea, los bytes 2, 3 y 4 rotan 1 byte a la izquierda. El primer byte pasa a ser el último.
  • (fila 2) En la tercera línea, los bytes 3 y 4 rotan 2 bytes a la izquierda. Los primeros 2 bytes pasan a ser los últimos.
  • (fila 3) En la cuarta línea, el byte 4 rotará 3 bytes a la izquierda. Los primeros 3 bytes pasan a ser los últimos.
GIF — ShiftRows

Como resultado tendremos la siguiente tabla STATE, el cual la usaremos para el siguiente proceso, MixColumns(state):

tabla resultante

3 MixColumns(state): Cada celda de cada columna de la STATE, son módulo multiplicados por una matriz dada usando un método conocido como; Rijndael Galois Field.

Aquí muestro mejor que columna de la tabla STATE multiplica por las filas de la matriz M:

la tabla M es siempre la misma

Visto de forma visual vamos a ver en detalle como quedaría la primera columna de la tabla resultante STATE:

*Las sumas de la multiplicación son un poco especiales, ya que NO generan/propagan carry

[0][0]: d4*2 ⊕ bf*3 ⊕ 5d*1 ⊕ 30*1 = 4 (Purpura*Amarillo)
[1][0]: d4*1 ⊕ bf*2 ⊕ 5d*3 ⊕ 30*1 = 66 (Purpura*Negro)
[2][0]: d4*1 ⊕ bf*1 ⊕ 5d*2 ⊕ 30*3 = 81 (Purpura*Gris)
[3][0]: d4*3 ⊕ bf*1 ⊕ 5d*1 ⊕ 30*2 = e5 (Purpura*Morado)

[0][1]: e0*2 ⊕ b4*3 ⊕ 52*1 ⊕ ae*1 = e0 (Azul*Amarillo)
[1][1]: e0*1 ⊕ b4*2 ⊕ 52*3 ⊕ ae*1 = cb (Azul*Negro)
[2][1]: e0*1 ⊕ b4*1 ⊕ 52*2 ⊕ ae*3 = 19 (Azul*Gris)
[3][1]: e0*3 ⊕ b4*1 ⊕ 52*1 ⊕ ae*2 = 9a (Azul*Morado)

[0][2]: b8*2 ⊕ 41*3 ⊕ 11*1 ⊕ f1*1 = 48 (Verde*Amarillo)
[1][2]: b8*1 ⊕ 41*2 ⊕ 11*3 ⊕ f1*1 = f8 (Verde*Negro)
[2][2]: b8*1 ⊕ 41*1 ⊕ 11*2 ⊕ f1*3 = d3 (Verde*Gris)
[3][2]: b8*3 ⊕ 41*1 ⊕ 11*1 ⊕ f1*2 = 7a (Verde*Morado)

[0][3]: 1e*2 ⊕ 27*3 ⊕ 98*1 ⊕ e5*1 = 28 (Rojo*Amarillo)
[1][3]: 1e*1 ⊕ 27*2 ⊕ 98*3 ⊕ e5*1 = 6 (Rojo*Negro)
[2][3]: 1e*1 ⊕ 27*1 ⊕ 98*2 ⊕ e5*3 = 26 (Rojo*Gris)
[3][3]: 1e*3 ⊕ 27*1 ⊕ 98*1 ⊕ e5*2 = 4c (Rojo*Morado)

Aquí muestro GIF, de la implementación (obviando algunos pasos):

GIF — MixColumns

Finalmente tenemos la siguiente tabla STATE:

tabla resultante

4 XOR con k[n]: Esta XOR se hace con una segunda llave (k[1]) derivada de la llave original (explico el proceso de generación de llaves más adelante), y una vez tengamos la k[1], vamos a hacer una XOR con todas las tablas STATE de los procesos anteriores, pero primero vamos a generar las llaves k[n].
En total, generaremos 10 llaves partiendo de la llave original, siguiendo este proceso:

  1. La llave original (k[0] aparece con el número 1).
  2. Reorganizamos los últimos 4 Bytes de la llave k[0].
  3. Sustituimos cada Byte usando la tabla SBox.
  4. Hacemos una XOR con los 4 Bytes de RCON1.
  5. Resultado del XOR anterior.
  6. Hacemos otro XOR con los primeros 4 bytes de la clave original.
  7. Finalmente, tenemos los primeros 4 bytes de la nueva clave (k[1]).

Bien, vamos a verlo de forma visual con el siguiente GIF:

GIF — XOR

Ahora que tenemos las 10 llaves k[n], para esta iteración vamos a hacer una XOR entre; STATE_inicial ⊕ STATE_SubBytes ⊕ STATE_ShiftRows ⊕ STATE_MixColumns ⊕ k[n] = STATE
Este nos devuelve un STATE, el cual será el STATE_inicial para la siguiente iteración/ronda.

(2) Final Round

Ahora tendríamos que iterar 9 veces más volviendo al (0.1) Procesos de encriptación, pero aquí te muestro el resultado completo:

Al final obtenemos el criptograma c:

Mensaje m encriptado utilizando la llave k

--

--

Massin Laa.

Bienvenid@!! Aquí escribo todo lo que voy aprendiendo y resumen de algunas de mis notas