Calcula a mano las tres operaciones de la ronda de AES y descubre por qué MixColumns esconde un código MDS, como los de Reed-Solomon, con branch number 5.
Al terminar sabrás:
Calcular SubBytes como inverso en el cuerpo de 256 elementos seguido de una transformación afín
Aplicar ShiftRows y MixColumns a un estado 4 × 4
Explicar qué aportan la no linealidad y la difusión a una ronda
Reconocer en MixColumns un código MDS de longitud 8, dimensión 4 y distancia 5 sobre el cuerpo de 256 elementos
Si sabes qué es un número en binario y sumar módulo 2 (sin llevadas), puedes seguirlo. Lo demás (hexadecimal, XOR, permutaciones, polinomios módulo 2 y cuerpos finitos) se explica sobre la marcha.
En la lección anterior vimos AES desde arriba: un estado de 4×4 bytes, un AddRoundKey inicial y diez rondas. Dentro de la caja negra quedaban tres operaciones sin abrir. Hoy las abrimos. Seguimos en el punto del relato en que AES sustituye a DES, y la matemática que va a hacer el trabajo es la de los cuerpos finitos F28 de la lección 9. Más abajo hay una sorpresa para quien conozca los códigos correctores: una de las tres operaciones es, por dentro, un código MDS, la misma propiedad que hace óptimos a los códigos de Reed-Solomon.
La idea
Una ronda de AES reparte el trabajo entre tres operaciones, cada una con un cometido distinto.
SubBytes cambia cada byte por otro, byte a byte, con una tabla (una caja S, de substitution box). Es la única operación no lineal de AES. Sin ella, todo el cifrado sería un sistema de ecuaciones lineales y se resolvería con álgebra de bachillerato. Es la confusión de Shannon.
ShiftRows desplaza las filas del estado. No cambia ningún valor: solo lleva cada byte a otra columna.
MixColumns mezcla los cuatro bytes de cada columna. Es la difusión: un byte que cambia contagia a toda su columna.
La combinación es lo que hace falta. SubBytes sola no mueve nada de sitio. ShiftRows y MixColumns, juntas, hacen que tras dos rondas cada byte de salida dependa de los 16 de entrada.
SubBytes: inverso y transformación afín
SubBytes aplica la misma función S a cada uno de los 16 bytes. Se calcula en dos pasos.
Inverso. Se toma el inverso multiplicativo del byte en F28, y el 00, que no tiene inverso, se deja en 00.
Transformación afín. El resultado b=(b7…b0) se pasa por una transformación lineal sobre F2 y se le suma la constante 63.
Repaso Transformación afín
Una transformación lineal sobre F2 (en el sentido de la lección 3: respeta el XOR) multiplica un vector de bits por una matriz, sumando módulo 2. Una transformación afín hace eso y además suma un vector fijo. Aquí el vector son los bits de 63=01100011. Calcular un bit de salida es sumar módulo 2 unos pocos bits de entrada, sin llevadas.
En FIPS 197 la transformación afín es, con los bits en columna (b0,…,b7)T:
Dicho de otro modo, bi′=bi⊕bi+4⊕bi+5⊕bi+6⊕bi+7⊕ci, con los índices módulo 8 y c=63. (Hemos comprobado con un programa que esta matriz da la misma tabla de 256 valores que la definición.)
Ejemplo: S(53). En la lección 9 vimos que 53−1=ca=11001010, es decir, b7…b0=1,1,0,0,1,0,1,0. Aplicamos la fórmula bit a bit:
i
bits que se suman: bi,bi+4,bi+5,bi+6,bi+7
parte lineal
ci
bi′
0
b0,b4,b5,b6,b7=0,0,0,1,1
0
1
1
1
b1,b5,b6,b7,b0=1,0,1,1,0
1
1
0
2
b2,b6,b7,b0,b1=0,1,1,0,1
1
0
1
3
b3,b7,b0,b1,b2=1,1,0,1,0
1
0
1
4
b4,b0,b1,b2,b3=0,0,1,0,1
0
0
0
5
b5,b1,b2,b3,b4=0,1,0,1,0
0
1
1
6
b6,b2,b3,b4,b5=1,0,1,0,0
0
1
1
7
b7,b3,b4,b5,b6=1,1,0,0,1
1
0
1
Leyendo la columna «parte lineal» de abajo arriba (b7 primero) sale 10001110=8e. Al sumarle la constante queda 8e⊕63=11101101=ed. Así que S(53)=ed, el mismo valor que da la tabla de FIPS 197.
Nadie calcula la caja S así cada vez: los 256 valores están en una tabla de 16×16, la tabla 4 de FIPS 197 (§5.1.1). Para buscar un byte, su primer dígito hexadecimal da la fila y el segundo la columna: 53 está en la fila 5, columna 3.
¿Por qué las dos piezas? El inverso es una función muy no lineal, y la afín evita que S tenga puntos fijos (S(a)=a) o «anti-fijos» (S(a) igual a a con todos los bits cambiados): se ha comprobado que no hay ninguno. S es una biyección de los 256 bytes, de modo que se puede deshacer (lección 12).
ShiftRows
La fila r del estado (con r=0,1,2,3) se rota r posiciones a la izquierda. La fila 0 no se toca, la 1 se rota una posición, la 2 dos y la 3 tres. Como el estado se rellena por columnas, esto hace que los cuatro bytes de cada columna de salida procedan de cuatro columnas distintas de la entrada.
MixColumns
MixColumns trata cada columna del estado como un vector de cuatro elementos de F28 y lo multiplica por una matriz fija M, con las operaciones del cuerpo:
La matriz es circulante: cada fila es la anterior desplazada una posición a la derecha. Recuerda que en F28 el byte 02 es el polinomio x, y 03=x+1. Multiplicar por 02 es la operación xtime de la lección 9 (desplazar un bit a la izquierda y, si se sale uno, hacer XOR con 1b). Multiplicar por 03 es 02⋅s⊕s. Multiplicar por 01 no hace nada. La suma es el XOR.
Ejemplo resuelto: MixColumns de una columna
Tomamos la columna (db,13,53,45). Primero los productos auxiliares, con xtime:
s
02⋅s
03⋅s=02⋅s⊕s
db
ad
ad⊕db=76
13
26
26⊕13=35
53
a6
a6⊕53=f5
45
8a
8a⊕45=cf
Por ejemplo, db=11011011; desplazado a la izquierda es 110110110, se sale un bit, y se hace XOR con 1b: b6⊕1b=ad. En cambio, 13=00010011 se desplaza a 26 sin salirse, y no hay XOR.
Ahora cada fila de M:
s0′=02⋅db⊕03⋅13⊕53⊕45=ad⊕35⊕53⊕45=8e.
s1′=db⊕02⋅13⊕03⋅53⊕45=db⊕26⊕f5⊕45=4d.
s2′=db⊕13⊕02⋅53⊕03⋅45=db⊕13⊕a6⊕cf=a1.
s3′=03⋅db⊕13⊕53⊕02⋅45=76⊕13⊕53⊕8a=bc.
Resultado: (db,13,53,45)↦(8e,4d,a1,bc), que es el ejemplo de FIPS 197.
MixColumns es un código MDS
Repaso Si no has hecho el curso de códigos
Un código de longitud 8 y dimensión 4 sobre F28 es un conjunto de palabras de 8 bytes que se construyen a partir de 4 bytes libres. Aquí la palabra es (a,Ma). Su distancia mínima es el menor número de bytes no nulos de una palabra no nula. Ningún código así puede tener distancia mayor que 8−4+1=5 (cota de Singleton), y los que la alcanzan se llaman MDS. Un menor de M es el determinante de una submatriz cuadrada que se obtiene quedándose con algunas filas y otras tantas columnas.
Un cambio en un solo byte de la columna ¿a cuántos bytes de salida llega? Con el byte (01,00,00,00) la salida es (02,01,01,03): llega a los cuatro. Y con dos bytes distintos de cero, por ejemplo (01,01,00,00), la salida es (01,03,00,02): llega a tres, que sumados a los dos de entrada hacen cinco bytes distintos de cero. Siempre se cumple:
wt(a)+wt(Ma)≥5para toda columna a=0,
donde wt es el número de bytes no nulos. Ese mínimo se llama branch number de MixColumns, y vale 5. Es el máximo posible para una matriz 4×4.
La razón está en la lección de códigos de Reed-Solomon. Mira las palabras (a,Ma) de ocho bytes: forman un código sobre F28 de longitud 8 y dimensión 4. Su distancia mínima es justo el mínimo de wt(a)+wt(Ma), es decir, el branch number. Y un código cuyas palabras son (a,Aa) es MDS (alcanza la cota de Singleton d=n−k+1 de la lección sobre errores y borrones) si y solo si todos los menores cuadrados de A son distintos de cero. Aquí n−k+1=8−4+1=5.
Lo hemos comprobado con un programa: los 16+36+16+1=69 menores cuadrados de M (de tamaños 1,2,3 y 4) son no nulos en F28; en particular detM=01. Así que el código de las palabras (a,Ma) es MDS, con longitud 8, dimensión 4 y distancia 5 (un código [8,4,5]) sobre F28, como los Reed-Solomon, y su branch number es 5. Daemen y Rijmen, los autores de Rijndael, eligieron esta matriz precisamente por eso: en su estrategia de la huella ancha (wide trail strategy) necesitan que la difusión sea la mayor posible, y un código MDS la da. Ojo con el «como»: comparte con los Reed-Solomon la propiedad MDS, pero no es un código de Reed-Solomon, ni siquiera en su versión generalizada. Una forma de verlo: si multiplicas sus palabras coordenada a coordenada, los productos generan un espacio de dimensión 8, y con cualquier Reed-Solomon (generalizado) de longitud 8 y dimensión 4 solo llegan a dimensión 2⋅4−1=7 (lo hemos comprobado con un programa). Otra diferencia es el uso: allí la redundancia sirve para recuperar bytes perdidos, y aquí, para que un cambio local se extienda a toda la columna.
Ejemplo resuelto: la ronda 1 entera
Retomamos el ejemplo de FIPS 197 de la lección anterior. El estado al inicio de la ronda 1 (tras el primer AddRoundKey) es
193de3bea0f4e22b9ac68d2ae9f84808.
SubBytes. Cada byte pasa por S. Los valores salen de la tabla 4 de FIPS 197; para este ejemplo te bastan estos 16:
columna
SubBytes
0
19↦d4, 3d↦27, e3↦11, be↦ae
1
a0↦e0, f4↦bf, e2↦98, 2b↦f1
2
9a↦b8, c6↦b4, 8d↦5d, 2a↦e5
3
e9↦1e, f8↦41, 48↦52, 08↦30
Sale
d42711aee0bf98f1b8b45de51e415230.
ShiftRows. La fila 1 se rota una posición, la 2 dos y la 3 tres:
d4bf5d30e0b452aeb84111f11e2798e5.
MixColumns. Cada columna se multiplica por M. La primera, (d4,bf,5d,30), da (04,66,81,e5). Por ejemplo, 02⋅d4=b3 y 03⋅bf=da. El estado completo es
046681e5e0cb199a48f8d37a2806264c.
AddRoundKey. Se hace XOR con la clave de ronda 1 (la calcularemos en la lección siguiente):
Esa es la entrada de la ronda 2. En una sola ronda ya se ve la difusión: cada byte de la primera columna del resultado depende de cuatro bytes del estado inicial, que venían de cuatro columnas distintas.
Ejercicios
Ejercicio 1
Calcula S(01) con la definición (inverso y transformación afín).
Ver la solución
El inverso de 01 es 01 (el uno del cuerpo). Sus bits son b0=1 y los demás 0. En bi′=bi⊕bi+4⊕bi+5⊕bi+6⊕bi+7⊕ci el único bit que vale 1 es b0, que aparece en los índices i con i∈{0,4,3,2,1} (porque i+4, i+5, i+6 o i+7 valen 0 módulo 8 cuando i=4,3,2,1). Así que la parte lineal da b4′…b0′=11111, es decir, 1f. Sumamos la constante: 1f⊕63=7c. Por tanto S(01)=7c.
Ejercicio 2
Aplica ShiftRows al estado
00102030011121310212223203132333.Ver la solución
La fila 0 se queda igual. La fila 1, (10,11,12,13), rota una posición a la izquierda: (11,12,13,10). La fila 2 rota dos: (22,23,20,21). La fila 3 rota tres: (33,30,31,32).
00112233011223300213203103102132
Los bytes de la primera columna, 00,11,22,33, proceden de las cuatro columnas de la entrada.
Ejercicio 3
Calcula MixColumns de las columnas (01,00,01,00) y (00,00,01,01) y halla en cada caso wt(a)+wt(Ma). ¿Qué te dice sobre el branch number?
Ver la solución
Con a=(01,00,01,00), Ma es la suma de las columnas primera y tercera de M: (02⊕01,01⊕03,01⊕02,03⊕01)=(03,02,03,02). Pesos: 2+4=6.
Con a=(00,00,01,01), Ma es la suma de las columnas tercera y cuarta: (01⊕01,03⊕01,02⊕03,01⊕02)=(00,02,01,03). Pesos: 2+3=5.
La suma nunca baja de 5, pero puede ser mayor: aquí sale 6 en un caso y 5 en el otro. El branch number es un mínimo sobre todas las columnas no nulas, no el valor de cada una. Con las dos columnas del texto, (01,00,00,00) y (01,01,00,00), se alcanza exactamente 5, y por eso el mínimo es 5 y no más.
En resumen
SubBytes es el inverso en F28 seguido de una transformación afín con la constante 63: es la única parte no lineal de AES.
ShiftRows rota la fila r del estado r posiciones a la izquierda, y mezcla las columnas entre sí.
MixColumns multiplica cada columna por una matriz circulante con 02,03,01,01, operando en F28.
La matriz de MixColumns genera, con I4, un código MDS [8,4,5] sobre F28: su branch number es 5, el máximo posible, y de ahí sale la difusión (Daemen y Rijmen).
Una ronda es SubBytes, ShiftRows, MixColumns y AddRoundKey; solo falta saber de dónde salen las claves de ronda.