Jestem jednym z autorów Gimli. Mamy już wersję z 2 tweetami (280 znaków) w C, ale chciałbym zobaczyć, jak mała może być.
Gimli ( papier , strona internetowa ) to projekt z dużą szybkością i permutacją kryptograficzną o wysokim poziomie bezpieczeństwa, który zostanie zaprezentowany podczas Konferencji na temat sprzętu kryptograficznego i systemów wbudowanych (CHES) 2017 (25-28 września).
Zadanie
Jak zwykle: aby niewielka użyteczna implementacja Gimli w wybranym przez Ciebie języku.
Powinien być w stanie pobrać 384 bity (lub 48 bajtów lub 12 znaków bez znaku ...) i zwrócić (może zmodyfikować w miejscu, jeśli używasz wskaźników) wynik Gimli zastosowany na tych 384 bitach.
Dozwolona jest konwersja wejściowa z dziesiętnej, szesnastkowej, ósemkowej lub binarnej.
Potencjalne narożne skrzynki
Zakłada się, że kodowanie liczb całkowitych ma charakter endianowy (np. To, co prawdopodobnie już masz).
Możesz zmienić nazwę Gimli
na, G
ale wciąż musi to być wywołanie funkcji.
Kto wygrywa?
To jest golf golfowy, więc wygrywa najkrótsza odpowiedź w bajtach! Oczywiście obowiązują standardowe zasady.
Implementacja referencyjna znajduje się poniżej.
Uwaga
Pojawiły się pewne obawy:
„hej, proszę, zaimplementuj mój program za darmo w innych językach, aby nie musiałem” (podziękowania dla @jstnthms)
Moja odpowiedź jest następująca:
Z łatwością mogę to zrobić w Javie, C #, JS, Ocaml ... To jest więcej dla zabawy. Obecnie My (zespół Gimli) wdrożyliśmy (i zoptymalizowaliśmy) w AVR, Cortex-M0, Cortex-M3 / M4, Neon, SSE, SSE-Unrolled, AVX, AVX2, VHDL i Python3. :)
O Gimli
Stan
Gimli stosuje sekwencję rund do stanu 384-bitowego. Stan jest reprezentowany jako równoległościan o wymiarach 3 × 4 × 32 lub, równoważnie, jako macierz 3 × 4 32-bitowych słów.
Każda runda jest sekwencją trzech operacji:
- warstwa nieliniowa, w szczególności 96-bitowy moduł SP nałożony na każdą kolumnę;
- w każdej drugiej rundzie liniowa warstwa mieszająca;
- w co czwartej rundzie stały dodatek.
Warstwa nieliniowa.
SP-box składa się z trzech podoperacji: rotacji pierwszego i drugiego słowa; 3-wejściowa nieliniowa funkcja T; i zamiana pierwszego i trzeciego słowa.
Warstwa liniowa.
Warstwa liniowa składa się z dwóch operacji wymiany, mianowicie Small-Swap i Big-Swap. Small-Swap występuje co 4 rundy, zaczynając od 1. rundy. Big-Swap odbywa się co 4 rundy, począwszy od 3 rundy.
Stałe rundy.
Gimli ma 24 rundy o numerach 24,23, ..., 1. Gdy liczba rundy r wynosi 24,20,16,12,8,4 XOR, stała XOR (0x9e377900 XOR r) do pierwszego słowa stanu.
źródło odniesienia w 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);
}
}
}
Wersja tweetowana w C.
Być może nie jest to najmniejsza użyteczna implementacja, ale chcieliśmy mieć wersję standardową C (a zatem brak UB i „użyteczną” w bibliotece).
#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);}
Wektor testowy
Następujące dane wejściowe wygenerowane przez
for (i = 0;i < 12;++i) x[i] = i * i * i + i * 0x9e3779b9;
i „wydrukowane” wartości przez
for (i = 0;i < 12;++i) {
printf("%08x ",x[i])
if (i % 4 == 3) printf("\n");
}
a zatem:
00000000 9e3779ba 3c6ef37a daa66d46
78dde724 1715611a b54cdb2e 53845566
f1bbcfc8 8ff34a5a 2e2ac522 cc624026
powinien zwrócić:
ba11c85a 91bad119 380ce880 d24c2c68
3eceffea 277a921c 4f73a0bd da5a9cd8
84b673f0 34e52ff7 9e2bef49 f41bb8d6
-round
zamiast --round
oznacza, że nigdy nie wygasa. Konwersja --
na myślnik nie jest prawdopodobnie sugerowana w kodzie :)