En FIPS-197 (el Estándar de cifrado avanzado , conocido como AES), se utiliza mucho SubBytes
, lo que podría implementarse como
unsigned char SubBytes(unsigned char x) {
static const unsigned char t[256] = {
0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76,
0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0,
0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15,
0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75,
0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84,
0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF,
0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8,
0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2,
0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73,
0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB,
0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79,
0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08,
0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A,
0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E,
0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF,
0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16};
return t[x];}
Esta función no es arbitraria; Es un mapeo reversible, que consiste en una inversión en un campo de Galois seguido de una transformación afín. Todos los detalles están en FIPS-197 sección 5.1.1 o aquí sección 4.2.1 (bajo un nombre ligeramente diferente).
Un problema con la implementación como tabla es que se abre a los llamados ataques de sincronización de caché .
Por lo tanto, su misión es idear un reemplazo exacto para la SubBytes()
función anterior , que exhibe un comportamiento de tiempo constante; asumiremos que ese es el caso cuando no se usa nada dependiendo de la entrada x
de SubBytes
:
- como un índice de matriz,
- como el control operando de
if
,while
,for
,case
, o el operador?:
; - como cualquier operando de operadores
&&
,||
,!
,==
,!=
,<
,>
,<=
,>=
,*
,/
,%
; - como el operando derecho de los operadores
>>
,<<
,*=
,/=
,%=
,<<=
,>>=
.
El ganador será uno con el costo más bajo, obtenido a partir del número de operadores ejecutados en la ruta de datos de entrada-dependiente, con un peso de 5 para los operadores unarios -
y ~
así como para <<1
, >>1
, +1
, -1
; peso de 7 para todos los demás operadores, turnos con otros recuentos o adiciones / subscripciones de otras constantes (los tipos de lanzamientos y promociones son gratuitos). Por principio, ese costo no cambia al desenrollar los bucles (si los hay), y es independiente de la entrada x
. Como factor decisivo, gana la respuesta con el código más corto después de eliminar espacios en blanco y comentarios.
Planeo designar una entrada como respuesta lo antes posible en el año 2013, UTC. Consideraré las respuestas en los idiomas que conozco, clasificándolas como una traducción directa a C no optimizada para el tamaño.
Disculpas por la omisión inicial de +1
y -1
en los operadores favorecidos, de lanzamientos gratuitos y promociones, y clasificación de tamaño. Tenga en cuenta que *
está prohibido tanto cuando es unario como de multiplicación.
fuente
Respuestas:
Puntuación:
940933926910, enfoque de torre de campoLa estructura es esencialmente la misma que la implementación de Boyar y Peralta: reduzca la inversión en GF (2 ^ 8) a la inversión en GF (2 ^ 4), divídala en un prólogo lineal, un cuerpo no lineal y un epílogo lineal, y luego minimizarlos por separado. Pago algunas penalizaciones por la extracción de bits, pero lo compenso al poder realizar operaciones en paralelo (con un poco de relleno juicioso de los bits de
fwd
). Con más detalle...Antecedentes
Como se menciona en la descripción del problema, el cuadro S consiste en una inversión en una implementación particular del campo de Galois GF (2 ^ 8) seguido de una transformación afín. Si sabe lo que ambos significan, omita esta sección.
Una transformación afín (o lineal) es una función
f(x)
que respetaf(x + y) = f(x) + f(y)
yf(a*x) = a*f(x)
.Un campo es un conjunto
F
de elementos con dos elementos especiales, que llamaremos0
y1
, y dos operadores, que llamaremos+
y*
, que respeta varias propiedades. En esta sección, se supone quex
,y
yz
son elementos deF
.F
forma un grupo abeliano bajo+
con0
como la identidad: es decir,x + y
es un elemento deF
;x + 0 = 0 + x = x
; cada unox
tiene un correspondiente-x
tal quex + (-x) = (-x) + x = 0
;x + (y + z) = (x + y) + z
; yx + y
=y + x
.F
otro que0
forma un grupo abeliano bajo*
con1
como la identidad.x * (y + z) = (x * y) + (x * z)
.Resulta que hay algunas limitaciones bastante severas en los campos finitos:
p
está el primo yk
es el poder) .F\{0}
debajo*
es cíclico; es decir, hay al menos un elemento deg
tal manera que cada elemento es un poderg
.k
del campo del orden primo. Por ejemplo, GF (2 ^ 8) tiene una representación en términos de polinomios dex
más de GF (2). De hecho, generalmente hay más de una representación. Considerex^7 * x
en GF (2 ^ 8); debe ser equivalente a algún polinomio de orden 7, pero ¿cuál? Hay muchas opciones que dan la estructura correcta; AES elige hacerx^8 = x^4 + x^3 + x + 1
(el polinomio lexicográficamente más pequeño que funciona).Entonces, ¿cómo calculamos un inverso en esa representación particular de GF (2 ^ 8)? Es un problema demasiado difícil de abordar directamente, por lo que debemos desglosarlo.
Torres de campo: representando GF (2 ^ 8) en términos de GF (2 ^ 4)
En lugar de representar GF (2 ^ 8) con polinomios de 8 términos sobre GF (2), podemos representarlo con polinomios de 2 términos sobre GF (2 ^ 4). Esta vez tenemos que elegir un polinomio lineal para
x^2
. Supongamos que elegimosx^2 = px + q
. Entonces(ax + b) * (cx + d) = (ad + bc + acp)x + (bd + acq)
.¿Es más fácil encontrar un inverso en esta representación? Si
(cx + d) = (ax + b)^-1
obtenemos las ecuaciones simultáneasad + (b + ap)c = 0
bd + (aq)c = 1
Dejar
D = [b(b+ap) + a^2 q]
y ponerc = a * D^-1
;d = (b + ap) * D^-1
. Entonces podemos hacer un inverso en GF (2 ^ 8) por el costo de una conversión a GF (2 ^ 4), un inverso y algunas adiciones y multiplicaciones en GF (2 ^ 4), y una conversión de regreso. Incluso si hacemos lo inverso por medio de una tabla, hemos reducido el tamaño de la tabla de 256 a 16.Detalles de implementacion
Para construir una representación de GF (4) podemos elegir entre tres polinomios para reducir
x^4
:x^4 = x + 1
x^4 = x^3 + 1
x^4 = x^3 + x^2 + x + 1
La diferencia más importante está en la implementación de la multiplicación. Para cualquiera de los tres (que corresponden a
poly
3, 9, f) funcionará lo siguiente:Sin embargo, si elegimos
poly = 3
, podemos manejar el desbordamiento de manera mucho más eficiente porque tiene una estructura agradable: no hay doble desbordamiento, porque las dos entradas son cúbicas yx^6 = x^2 (x + 1)
también son cúbicas. Además, podemos guardar los cambios deb
: ya que estamos dejando que el desbordamiento dure,a0 x^2
no tiene ningún bit establecido correspondiente ax
o 1, por lo que podemos enmascararlo con -4 en lugar de -1. El resultado esTodavía tenemos que elegir los valores de
p
yq
para la representación de GF (2 ^ 8) sobre GF (2 ^ 4), pero no tenemos muchas restricciones sobre ellos. Lo único que importa es que podemos obtener una función lineal de los bits de nuestra representación original a los bits de la representación de trabajo. Esto es importante por dos razones: en primer lugar, es fácil realizar transformaciones lineales, mientras que una transformación no lineal requeriría una optimización equivalente en dificultad a la optimización de todo el S-box; En segundo lugar, porque podemos obtener algunos beneficios secundarios. Para recapitular la estructura:Si los bits de
x
sonx7 x6 ... x0
entonces, dado que la transformación es lineal, obtenemosa = f({x7}0000000 + 0{x6}000000 + ... + 0000000{x0}) = f({x7}0000000) + f(0{x6}000000) + ... + f(0000000{x0})
. Ajústalo y obtenemosa^2 = f({x7}0000000)^2 + f(0{x6}000000)^2 + ... + f(0000000{x0})^2
donde se cancelan los términos cruzados (porque en GF (2),1 + 1 = 0
). Pora^2
lo tanto , también se puede calcular como una función lineal dex
. Podemos aumentar la transformación lineal directa para obtener:y tenemos tres multiplicaciones y una suma. También podemos extender el código de multiplicación para hacer las dos multiplicaciones
Dinv
en paralelo. Por lo tanto, nuestro costo total es una transformación lineal hacia adelante, una suma, dos multiplicaciones, una inversa en GF (2 ^ 4) y una transformación lineal hacia atrás. Podemos lanzar la transformación lineal post-inversa del S-box y obtener eso esencialmente gratis.El cálculo de los coeficientes para las transformaciones lineales no es muy interesante, y tampoco lo es la microoptimización para guardar una máscara aquí y un cambio allí. La parte interesante restante es la optimización de
inverse_GF16
. Hay 2 ^ 64 funciones diferentes de 4 bits a 4 bits, por lo que una optimización directa requiere mucha memoria y tiempo. Lo que he hecho es considerar 4 funciones de 4 bits a 1 bit, poner un límite al costo total permitido para cualquier función (con un costo máximo de 63 por función puedo enumerar todas las expresiones adecuadas en menos de un minuto), y para cada tupla de funciones, elimine la subexpresión común. Después de 25 minutos de procesamiento, encuentro que el mejor inverso posible con ese límite tiene un costo total de 133 (un promedio de 33.25 por bit de salida, lo cual no es malo teniendo en cuenta que la expresión más barata para cualquier bit individual es 35) .Todavía estoy experimentando con otros enfoques para minimizar la inversión en GF (2 ^ 4), y un enfoque que construye de abajo hacia arriba en lugar de de arriba hacia abajo ha arrojado una mejora de 133 a 126.
fuente
& 1
se puede recortar (especialmente six
esunsigned char
;CHAR_BIT
es 8 en codegolf).Puntuación: 980 = 7 * 5 + 115 * 7 + 7 * 5 + 15 * 7, minimización de Boyar y Peralta
Encontré una nueva técnica de minimización de lógica combinacional con aplicaciones a la criptología de Joan Boyar y René Peralta, que (salvo el formalismo C) hace lo que se requiere. La técnica utilizada para derivar sus ecuaciones está patentada por nada menos que Estados Unidos. Acabo de hacer una traducción C directa de sus ecuaciones , amablemente vinculada aquí .
fuente
Puntuación: 10965
Esto utiliza el mismo principio de desenrollar la búsqueda de matriz. Puede requerir moldes adicionales.
Gracias a ugoren por señalar cómo mejorar
is_zero
.fuente
(c|-c)>>31
es 0 para 0 y -1 en caso contrario.c >> 4
parece un giro a la derecha firmado para mí. Y si realmente insistes, lo((unsigned int)(c|-c))>>31
esc?1:0
.c >>4
obras con o sin extensión de signo. Pero es una buena idea usar un turno sin firmar: se editará cuando llegue a casa y pueda usar una computadora adecuada en lugar de un teléfono. Gracias.Puntuación: 9109, enfoque algebraico
Dejaré el enfoque de búsqueda en caso de que alguien pueda mejorarlo drásticamente, pero resulta que es posible un buen enfoque algebraico. Esta implementación encuentra el inverso multiplicativo usando el algoritmo de Euclides . Lo escribí en Java pero, en principio, puede portarse a C: he comentado las partes que cambiarían si desea volver a trabajarlo utilizando solo tipos de 8 bits.
Gracias a ugoren por señalar cómo acortar el
is_nonzero
cheque en un comentario sobre mi otra respuesta.fuente
Puntuación: 256 * (7+ (8 * (7 + 7 + 7) - (2 + 2)) + 5 + 7 + 7) = 48640 (asumiendo bucles desenrollados)
Explicación:
Esencialmente, una búsqueda de matriz se vuelve a implementar utilizando operadores bit a bit y siempre procesando toda la matriz. Recorremos la matriz y xor
x
con cada índice, luego usamos operadores bit a bit para negar lógicamente el resultado, por lo que obtenemos 255 cuandox=i
y 0 de lo contrario. Hacemos bit a bit, y esto con el valor de la matriz, de modo que el valor elegido no cambia y los demás se convierten en 0. Luego tomamos el bit a bit o de esta matriz, sacando así solo el valor elegido.Las dos
1 << j
operaciones desaparecerían como parte del desenrollado del bucle, reemplazándolas con las potencias de 2 de 1 a 128.fuente
-(2+2)
viene el cálculo de puntuación? Editar: ah, la creación de líneas crea ay<<1
a>>1
.Puntuación
19681692, usando la tabla de búsquedaNota: Esta solución no pasa los criterios debido a
w >> b
.Usando la tabla de búsqueda, pero leyendo 8 bytes a la vez.
3 * 7 + 32 * (6 * 7 + 2 * 5) + 7 = 692
fuente
w>>b
RHS ha calculado a partir dex
w >> b
dondeb
depende de la entrada; Tambiénx/8
,x%8
y*= (1-badi)
. El primero es especialmente probable que degenere en una dependencia de tiempo en las CPU de gama baja. Sin embargo, la idea de usar variables amplias ciertamente tiene potencial.w >> b
es bastante esencial (que necesito para ver si se puede solucionar sin tener que reescribir todo.Tabla de búsqueda y máscara, puntaje = 256 * (5 * 7 + 1 * 5) = 10240
Utiliza la búsqueda de tablas con una máscara para seleccionar solo el resultado que queremos. Utiliza el hecho de que
j|-j
es negativo (cuando i! = X) o cero (cuando i == x). Shifting crea una máscara de todo uno o de cero, que se utiliza para seleccionar solo la entrada que queremos.fuente