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 :)Respostas:
CJam (114 caracteres)
Este é um bloco anônimo (função): se você quiser nomeá-lo
G
, acrescente:G
. No CJam, os nomes atribuídos podem ser apenas letras maiúsculas. Há espaço para acrescentar um comentárioe# Gimli in CJam
e deixar os caracteres em um único tweet.Teste online
Dissecação
fonte
C (gcc), 237 bytes
Eu provavelmente ganhei bytes com meu método de troca, mas é muito fofo para não usar.
fonte
unsigned
vez deuint32_t
(e o código do OP estava trapaceiro de usarlong
) porque a idéia por trás da cifra é que é altamente portátil. (De fato, fundamentalmente, isso economiza apenas 8 bytes).gcc
uma CPU Intel de 32 ou 64 bits (e provavelmente muito mais).C, 268 caracteres (268 bytes) usando uint32_t
NB Como o código original usa
<stdint.h>
e digitaS
comouint32_t *
, acho que o uso delong
é uma trapaça para chegar a 280 caracteres à custa da portabilidade, que é a razão do usouint32_t
em primeiro lugar. Se para fins de comparação, exigimos o uso consistenteuint32_t
e a assinatura explícitavoid gimli(uint32_t *)
, o código original é realmente 284 caracteres e o código do orlp é 276 caracteres.Isso pode ser dividido em dois tweets com marcadores de continuação, como
e
fonte
long
na minha versão é seguro (com relação à portabilidade) porque o tamanho mínimo de um comprimento é 32 bits pelo padrão (em oposição aint
). As rotaçõesx
ey
são feitas antes da conversão paralong
a tarefa, tornando-as seguras (como a mudança correta no valor assinado depende do CC). O elenco ao voltar para ouint32_t* S
) se livra dos bits superiores e nos coloca no estado correto :).Java (OpenJDK 8) ,
351343339320318247 + 56 bytesApenas uma porta 1: 1 próxima da referência para começar a jogar.
Experimente online!
fonte
Integer
? o_O Desde que você não usar qualquerInteger
método, não há nenhuma razão para não usarint
s aqui ...s[0]^=(0x9e377900|r);
(no final) - você não pode soltar os parênteses extras?s[4+c]>>>(23)
.void P(int[]S,int a,int b){int x=S[a];S[a]=S[b];S[b]=x;}void gimli(int[]S){for(int r=24,c,x,y,z;r>0;S[0]^=y<1?0x9e377901+r:0){for(c=4;c-->0;){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);}y=r%4;if(--r%2>0){P(S,0,1+y/2);P(S,3,2-y/2);}}}
. Eu basicamente fiz as alterações mínimas necessárias para compilar. As regras de precedência do Java não são muito diferentes das do C.JavaScript (ES6), 231 bytes
Demo
Mostrar snippet de código
fonte
Assembler x86 de 32 bits (112 bytes)
(convenção de chamada __cdecl)
Versão Tweetable (codificação Base85 no formato z85):
fonte