Eu sou um dos autores de Gimli. Já temos uma versão de 2 tweets (280 caracteres) em C, mas eu gostaria de ver como ela pode ficar pequena.
Gimli ( documento , site ) é uma alta velocidade com design de permutação criptográfica de alto nível de segurança que será apresentada na Conferência sobre Hardware Criptográfico e Sistemas Incorporados (CHES) 2017 (25 a 28 de setembro).
A tarefa
Como de costume: para tornar a implementação reduzida do Gimli no idioma de sua escolha.
Ele deve receber 384 bits de entrada (ou 48 bytes ou 12 int sem sinal ...) e retornar (pode modificar no local se você usar ponteiros) o resultado do Gimli aplicado nesses 384 bits.
É permitida a conversão de entrada de decimal, hexadecimal, octal ou binário.
Casos de canto em potencial
Supõe-se que a codificação inteira seja little-endian (por exemplo, o que você provavelmente já possui).
Você pode renomear Gimli
para, G
mas ainda deve ser uma chamada de função.
Quem ganha?
Isso é código-golfe, então a resposta mais curta em bytes vence! Regras padrão se aplicam, é claro.
Uma implementação de referência é fornecida abaixo.
Nota
Alguma preocupação foi levantada:
"ei galera, implemente meu programa gratuitamente em outros idiomas para não precisar" (thx para @jstnthms)
Minha resposta é a seguinte:
Eu posso fazê-lo facilmente em Java, C #, JS, Ocaml ... É mais por diversão. Atualmente, nós (a equipe Gimli) implementamos (e otimizamos) o AVR, o Cortex-M0, o Cortex-M3 / M4, o Neon, o SSE, o SSE, o desenrolamento do SSE, o AVX, o AVX2, o VHDL e o Python3. :)
Sobre Gimli
O Estado
Gimli aplica uma sequência de rodadas a um estado de 384 bits. O estado é representado como um paralelepípedo com dimensões 3 × 4 × 32 ou, equivalentemente, como uma matriz 3 × 4 de palavras de 32 bits.
Cada rodada é uma sequência de três operações:
- uma camada não linear, especificamente uma caixa SP de 96 bits aplicada a cada coluna;
- em cada segunda rodada, uma camada de mistura linear;
- em cada quarta rodada, uma adição constante.
A camada não linear.
A caixa SP consiste em três suboperações: rotações da primeira e da segunda palavras; uma função T não linear de 3 entradas; e uma troca da primeira e terceira palavras.
A camada linear.
A camada linear consiste em duas operações de swap, a saber, Small-Swap e Big-Swap. O Small-Swap ocorre a cada 4 rodadas, começando na 1ª rodada. O Big-Swap ocorre a cada 4 rodadas, começando na 3ª rodada.
As constantes redondas.
Há 24 rodadas em Gimli, numeradas 24,23, ..., 1. Quando o número da ronda r é 24,20,16,12,8,4, XOR é a constante da ronda (0x9e377900 XOR r) para a primeira palavra de estado.
fonte de referência em C
#include <stdint.h>
uint32_t rotate(uint32_t x, int bits)
{
if (bits == 0) return x;
return (x << bits) | (x >> (32 - bits));
}
extern void gimli(uint32_t *state)
{
int round;
int column;
uint32_t x;
uint32_t y;
uint32_t z;
for (round = 24; round > 0; --round)
{
for (column = 0; column < 4; ++column)
{
x = rotate(state[ column], 24);
y = rotate(state[4 + column], 9);
z = state[8 + column];
state[8 + column] = x ^ (z << 1) ^ ((y&z) << 2);
state[4 + column] = y ^ x ^ ((x|z) << 1);
state[column] = z ^ y ^ ((x&y) << 3);
}
if ((round & 3) == 0) { // small swap: pattern s...s...s... etc.
x = state[0];
state[0] = state[1];
state[1] = x;
x = state[2];
state[2] = state[3];
state[3] = x;
}
if ((round & 3) == 2) { // big swap: pattern ..S...S...S. etc.
x = state[0];
state[0] = state[2];
state[2] = x;
x = state[1];
state[1] = state[3];
state[3] = x;
}
if ((round & 3) == 0) { // add constant: pattern c...c...c... etc.
state[0] ^= (0x9e377900 | round);
}
}
}
Versão Tweetable em C
Essa pode não ser a menor implementação utilizável, mas queríamos ter uma versão padrão C (portanto, não UB e "utilizável" em uma biblioteca).
#include<stdint.h>
#define P(V,W)x=V,V=W,W=x
void gimli(uint32_t*S){for(long r=24,c,x,y,z;r;--r%2?P(*S,S[1+y/2]),P(S[3],S[2-y/2]):0,*S^=y?0:0x9e377901+r)for(c=4;c--;y=r%4)x=S[c]<<24|S[c]>>8,y=S[c+4]<<9|S[c+4]>>23,z=S[c+8],S[c]=z^y^8*(x&y),S[c+4]=y^x^2*(x|z),S[c+8]=x^2*z^4*(y&z);}
Vetor de teste
A seguinte entrada gerada por
for (i = 0;i < 12;++i) x[i] = i * i * i + i * 0x9e3779b9;
e valores "impressos" por
for (i = 0;i < 12;++i) {
printf("%08x ",x[i])
if (i % 4 == 3) printf("\n");
}
portanto:
00000000 9e3779ba 3c6ef37a daa66d46
78dde724 1715611a b54cdb2e 53845566
f1bbcfc8 8ff34a5a 2e2ac522 cc624026
deve retornar:
ba11c85a 91bad119 380ce880 d24c2c68
3eceffea 277a921c 4f73a0bd da5a9cd8
84b673f0 34e52ff7 9e2bef49 f41bb8d6
-round
vez de --round
significa que ele nunca termina. Convertendo --
para um traço provavelmente não é sugerido em código :)