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 :)Odpowiedzi:
CJam (114 znaków)
Jest to anonimowy blok (funkcja): jeśli chcesz go nazwać,
G
dołącz:G
. W CJam przypisanym nazwom mogą być tylko pojedyncze wielkie litery. Jest miejsce na dodanie komentarzae# Gimli in CJam
i pozostawienie znaków w jednym tweecie.Test online
Sekcja
źródło
C (gcc), 237 bajtów
Prawdopodobnie zyskałem bajty dzięki metodzie wymiany, ale jest to zbyt urocze, aby nie używać.
źródło
unsigned
zamiastuint32_t
(oraz kod OP był nieco oszukuje do użytkulong
), ponieważ Ideą szyfru jest to, że bardzo przenośny. (W rzeczywistości zasadniczo oszczędza to zaledwie 8 bajtów).gcc
na 32-bitowym lub 64-bitowym procesorze Intel (i prawdopodobnie o wiele więcej).C, 268 znaków (268 bajtów) przy użyciu uint32_t
Uwaga: Ponieważ oryginalny kod używa
<stdint.h>
i typówS
jakouint32_t *
, myślę, że użycielong
jest oszustwem, aby dostać się do 280 znaków kosztem przenośności, co jest powodem użyciauint32_t
w pierwszej kolejności. Jeśli dla uczciwości porównania potrzebujemy konsekwentnego użyciauint32_t
i wyraźnego podpisuvoid gimli(uint32_t *)
, oryginalny kod to tak naprawdę 284 znaki, a kod orlp to 276 znaków.Można to podzielić na dwa tweety ze znacznikami kontynuacji jako
i
źródło
long
w mojej wersji jest bezpieczne (pod względem przenośności), ponieważ minimalny rozmiar długiego standardu to 32 bity (w przeciwieństwie doint
). Obrotyx
iy
są wykonywane przed rzutem dolong
przydziału, co czyni je bezpiecznymi (ponieważ właściwe przesunięcie na podpisanej wartości zależy od CC). Obsada po powrocie douint32_t* S
) pozbywa się górnych bitów i wprowadza nas we właściwy stan :).Java (OpenJDK 8) ,
351343339320318247 + 56 bajtówTylko blisko 1: 1 port odniesienia, od którego można zacząć grać w golfa.
Wypróbuj online!
źródło
Integer
co w ogóle korzystać? o_O Ponieważ nie używasz żadnejInteger
metody, nie ma powodu, aby nie używaćint
tutaj ...s[0]^=(0x9e377900|r);
(na samym końcu) - nie możesz upuścić dodatkowych nawiasów?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);}}}
. Zasadniczo dokonałem minimalnych zmian niezbędnych do skompilowania. Zasady pierwszeństwa Java nie różnią się bardzo od zasad C.JavaScript (ES6), 231 bajtów
Próbny
Pokaż fragment kodu
źródło
32-bitowy asembler x86 (112 bajtów)
(__cdecl konwencja wywoływania)
Wersja z tweetowaniem (kodowanie Base85 w formacie Z85):
źródło