Le but de ce défi est de trouver une implémentation incroyablement courte de la fonction suivante p
, dans le langage de votre choix. Voici le code C l’implémentant (voir
ce lien TIO qui affiche également ses sorties) et une page wikipedia le contenant.
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];
}
Quel est p
p
est un composant de deux standards cryptographiques russes, à savoir la fonction de hachage Streebog et le chiffrement de bloc Kuznyechik . Dans cet article (et lors des réunions ISO), les concepteurs de ces algorithmes ont affirmé avoir généré le tableau pi
en choisissant des permutations aléatoires de 8 bits.
Implémentations "impossibles"
Il y en a permutations sur 8 bits. Par conséquent, pour une permutation aléatoire donnée, un programme qui l'implémente ne doit pas nécessiter moins de 1683 bits.
Cependant, nous avons trouvé plusieurs implémentations anormalement petites (que nous énumérons ici ), par exemple le programme C suivant:
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;}
qui ne contient que 158 caractères et tient donc dans 1264 bits. Cliquez ici pour voir que cela fonctionne.
Nous parlons d'une implémentation courte "impossible" parce que, si la permutation était la sortie d'un processus aléatoire (comme l'affirment ses concepteurs), un programme de cette durée n'existerait pas (voir cette page pour plus de détails).
Mise en oeuvre de référence
Une version plus lisible du code C précédent est:
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;
}
Le tableau k
est tel que k[x] = L(16-x)
, où L
est linéaire en ce sens L(x^y)==L(x)^L(y)
et où, comme en C, ^
désigne le XOR. Cependant, nous n'avons pas réussi à tirer parti de cette propriété pour raccourcir notre mise en œuvre. Nous ne connaissons aucune structure s
qui pourrait permettre une implémentation plus simple - sa sortie est toujours dans le sous-champ, c.-à- où l’exponentiation est effectuée dans le champ fini. Bien sûr, vous êtes absolument libre d'utiliser une expression plus simple de si vous en trouvez une!s
La boucle while correspond à l'évaluation d'un logarithme discret dans le corps fini à 256 éléments. Cela fonctionne via une recherche simple par force brute: la variable factice a
est définie pour être un générateur du corps fini, et elle est multipliée par ce générateur jusqu'à ce que le résultat soit égal à x
. Quand c'est le cas, nous avons c'est l
le journal discret de x
. Cette fonction n'est pas définie à 0, d'où le cas particulier correspondant à l' if
instruction.
La multiplication par le générateur peut être vue comme une multiplication par dans qui est alors réduite modulo le polynôme . Le rôle de l ' est de s'assurer que la variable reste sur 8 bits. Alternativement, nous pourrions utiliser , auquel cas ce pourrait être un type (ou tout autre type entier). D'autre part, il est nécessaire de commencer par ce que nous devons avoir quand est égal à 1.unsigned char
a
a=(a<<1)^(a>>7)*(256^29)
a
int
l=1,a=2
l=255
x
Plus de détails sur les propriétés de p
sont présentés dans notre article , avec un résumé de la plupart de nos optimisations pour obtenir la mise en œuvre courte précédente.
Règles
Proposez un programme qui implémente la fonction p
en moins de 1683 bits. Plus le programme est court, plus il est anormal, pour une langue donnée, plus c'est court, mieux c'est. Si votre langue est Kuznyechik, Streebog ou p
intégrée, vous ne pouvez pas les utiliser.
La métrique que nous utilisons pour déterminer la meilleure implémentation est la longueur du programme en octets. Nous utilisons la longueur de bit dans notre article académique, mais nous nous en tenons aux octets pour des raisons de simplicité.
Si votre langue ne dispose pas d' une notion claire de la fonction, l' argument ou de sortie, le codage est à vous de définir, mais des trucs comme codant pour la valeur pi[x]
que x
sont évidemment interdites.
Nous avons déjà soumis un document de recherche contenant nos conclusions sur ce sujet. Il est disponible ici . Toutefois, s’il est publié dans un lieu scientifique, nous nous ferons un plaisir de reconnaître les auteurs des meilleures implémentations.
En passant, merci à xnor pour son aide lors de la rédaction de cette question!
1683 bits at most
une restriction stricte [sic?] Ou un objectif?