Das Ziel dieser Herausforderung ist es, eine unglaublich kurze Implementierung der folgenden Funktion p
in der Sprache Ihrer Wahl zu finden. Hier ist C-Code, der es implementiert (siehe
diesen TIO-Link , der auch seine Ausgaben druckt) und eine Wikipedia-Seite, die es enthält.
unsigned char pi[] = {
252,238,221,17,207,110,49,22,251,196,250,218,35,197,4,77,
233,119,240,219,147,46,153,186,23,54,241,187,20,205,95,193,
249,24,101,90,226,92,239,33,129,28,60,66,139,1,142,79,
5,132,2,174,227,106,143,160,6,11,237,152,127,212,211,31,
235,52,44,81,234,200,72,171,242,42,104,162,253,58,206,204,
181,112,14,86,8,12,118,18,191,114,19,71,156,183,93,135,
21,161,150,41,16,123,154,199,243,145,120,111,157,158,178,177,
50,117,25,61,255,53,138,126,109,84,198,128,195,189,13,87,
223,245,36,169,62,168,67,201,215,121,214,246,124,34,185,3,
224,15,236,222,122,148,176,188,220,232,40,80,78,51,10,74,
167,151,96,115,30,0,98,68,26,184,56,130,100,159,38,65,
173,69,70,146,39,94,85,47,140,163,165,125,105,213,149,59,
7,88,179,64,134,172,29,247,48,55,107,228,136,217,231,137,
225,27,131,73,76,63,248,254,141,83,170,144,202,216,133,97,
32,113,103,164,45,43,9,91,203,155,37,208,190,229,108,82,
89,166,116,210,230,244,180,192,209,102,175,194,57,75,99,182,
};
unsigned char p(unsigned char x) {
return pi[x];
}
Was ist p
p
ist Bestandteil zweier russischer kryptographischer Standards, nämlich der Hash-Funktion Streebog und der Blockchiffre Kuznyechik . In diesem Artikel (und während ISO-Meetings) behaupteten die Entwickler dieser Algorithmen, dass sie das Array pi
durch Auswahl zufälliger 8-Bit-Permutationen generierten .
"Unmögliche" Implementierungen
Es gibt Permutationen auf 8 Bits. Für eine gegebene zufällige Permutation wird daher nicht erwartet, dass ein Programm, das sie implementiert, weniger als 1683 Bits benötigt.
Wir haben jedoch mehrere ungewöhnlich kleine Implementierungen gefunden (die wir hier auflisten ), zum Beispiel das folgende C-Programm:
p(x){unsigned char*k="@`rFTDVbpPBvdtfR@\xacp?\xe2>4\xa6\xe9{z\xe3q5\xa7\xe8",l=0,b=17;while(--l&&x^1)x=2*x^x/128*285;return l%b?k[l%b]^k[b+l/b]^b:k[l/b]^188;}
die nur 158 Zeichen enthält und somit in 1264 Bit passt. Klicken Sie hier, um zu sehen, dass es funktioniert.
Wir sprechen von einer "unmöglich" kurzen Implementierung, da, wenn die Permutation die Ausgabe eines zufälligen Prozesses wäre (wie von seinen Designern behauptet), ein Programm dieser kurzen Art nicht existieren würde (siehe diese Seite für weitere Details).
Referenzimplementierung
Eine besser lesbare Version des vorherigen C-Codes ist:
unsigned char p(unsigned char x){
unsigned char
s[]={1,221,146,79,147,153,11,68,214,215,78,220,152,10,69},
k[]={0,32,50,6,20,4,22,34,48,16,2,54,36,52,38,18,0};
if(x != 0) {
unsigned char l=1, a=2;
while(a!=x) {
a=(a<<1)^(a>>7)*29;
l++;
}
unsigned char i = l % 17, j = l / 17;
if (i != 0) return 252^k[i]^s[j];
else return 252^k[j];
}
else return 252;
}
Die Tabelle k
ist so, dass k[x] = L(16-x)
, wo L
in dem Sinne linear ist L(x^y)==L(x)^L(y)
, und wo, wie in C, ^
das XOR bezeichnet. Es ist uns jedoch nicht gelungen, diese Eigenschaft zu nutzen, um unsere Implementierung zu verkürzen. Wir kennen keine Struktur s
, die eine einfachere Implementierung ermöglichen könnte - ihre Ausgabe befindet sich jedoch immer im Unterfeld, dh wobei die Exponentiation im endlichen Feld erfolgt. Natürlich steht es Ihnen frei, einen einfacheren Ausdruck zu verwenden, falls Sie einen finden sollten!s
Die while-Schleife entspricht der Auswertung eines diskreten Logarithmus im endlichen Feld mit 256 Elementen. Es funktioniert über eine einfache Brute-Force-Suche: Die Dummy-Variable a
wird als Generator des endlichen Feldes festgelegt und mit diesem Generator multipliziert, bis das Ergebnis gleich ist x
. Wenn dies der Fall ist, haben wir l
das diskrete Protokoll von x
. Diese Funktion ist nicht in 0 definiert, daher der der if
Anweisung entsprechende Sonderfall .
Die Multiplikation mit dem Generator kann als Multiplikation mit in die dann modulo des Polynoms reduziert wird . Die Aufgabe von ist es sicherzustellen, dass die Variable auf 8 Bits bleibt. Alternativ könnten wir verwenden , in welchem Fall ein (oder ein anderer ganzzahliger) Typ sein könnte. Auf der anderen Seite ist es notwendig, mit zu beginnen, da wir haben müssen, wann gleich 1 ist.unsigned char
a
a=(a<<1)^(a>>7)*(256^29)
a
int
l=1,a=2
l=255
x
Weitere Details zu den Eigenschaften von p
finden Sie in unserem Artikel, in dem die meisten Optimierungen beschrieben werden, um die vorherige kurze Implementierung zu erhalten.
Regeln
Schlagen Sie ein Programm vor, das die Funktion p
in weniger als 1683 Bit implementiert . Je kürzer das Programm ist, desto anormaler ist es, je kürzer die Sprache ist, desto besser. Wenn Ihre Sprache Kuznyechik, Streebog oder p
ein eingebautes hat, können Sie sie nicht verwenden.
Die Metrik, mit der wir die beste Implementierung ermitteln, ist die Programmlänge in Byte. Wir verwenden die Bitlänge in unserer wissenschaftlichen Arbeit, aber der Einfachheit halber halten wir uns hier an Bytes.
Wenn Ihre Sprache nicht über eine klare Vorstellung von Funktion, Argument oder Ausgang, ist die Codierung an Ihnen zu definieren, aber Tricks wie kodieren den Wert pi[x]
als x
offensichtlich verboten.
Wir haben bereits ein Forschungspapier mit unseren Ergebnissen zu diesem Thema eingereicht. Es ist hier erhältlich . Sollte es jedoch an einem wissenschaftlichen Ort veröffentlicht werden, werden wir gerne die Autoren der besten Implementierungen auszeichnen.
Übrigens, danke an xnor für seine Hilfe beim Verfassen dieser Frage!
1683 bits at most
eine strenge Einschränkung oder das Ziel?