Je suis l'un des auteurs de Gimli. Nous avons déjà une version 2 tweet (280 caractères) en C mais j'aimerais voir à quel point elle peut être petite.
Gimli ( papier , site Web ) est une conception de permutation cryptographique à haut débit et à haut niveau de sécurité qui sera présentée à la Conférence sur le matériel cryptographique et les systèmes embarqués (CHES) 2017 (25-28 septembre).
La tâche
Comme d'habitude: pour rendre la petite implémentation utilisable de Gimli dans la langue de votre choix.
Il devrait pouvoir prendre en entrée 384 bits (ou 48 octets, ou 12 entier non signé ...) et retourner (peut modifier en place si vous utilisez des pointeurs) le résultat de Gimli appliqué sur ces 384 bits.
La conversion d'entrée décimale, hexadécimale, octale ou binaire est autorisée.
Boîtes de coin potentielles
Le codage entier est supposé être petit-endien (par exemple ce que vous avez probablement déjà).
Vous pouvez renommer Gimli
en G
mais il doit toujours s'agir d'un appel de fonction.
Qui gagne?
Il s'agit de code-golf, donc la réponse la plus courte en octets l'emporte! Les règles standard s'appliquent bien sûr.
Une implémentation de référence est fournie ci-dessous.
Remarque
Certaines préoccupations ont été exprimées:
"Hé gang, veuillez implémenter mon programme gratuitement dans d'autres langues pour ne pas avoir à le faire" (merci à @jstnthms)
Ma réponse est la suivante:
Je peux facilement le faire en Java, C #, JS, Ocaml ... C'est plus pour le fun. Actuellement, nous (l'équipe Gimli) l'avons implémenté (et optimisé) sur AVR, Cortex-M0, Cortex-M3 / M4, Neon, SSE, SSE-unrolled, AVX, AVX2, VHDL et Python3. :)
À propos de Gimli
L'état
Gimli applique une séquence de tours à un état de 384 bits. L'état est représenté comme un parallélépipède de dimensions 3 × 4 × 32 ou, de manière équivalente, comme une matrice 3 × 4 de mots de 32 bits.
Chaque tour est une séquence de trois opérations:
- une couche non linéaire, spécifiquement un SP-box 96 bits appliqué à chaque colonne;
- à chaque deuxième tour, une couche de mélange linéaire;
- à chaque quatrième tour, un ajout constant.
La couche non linéaire.
La SP-box se compose de trois sous-opérations: rotation des premier et deuxième mots; une fonction T non linéaire à 3 entrées; et un échange des premier et troisième mots.
La couche linéaire.
La couche linéaire se compose de deux opérations de swap, à savoir Small-Swap et Big-Swap. Small-Swap se produit tous les 4 tours à partir du 1er tour. Big-Swap se produit tous les 4 tours à partir du 3e tour.
Les constantes rondes.
Il y a 24 tours à Gimli, numérotés 24,23, ..., 1. Lorsque le nombre rond r est 24,20,16,12,8,4, nous XOR la constante ronde (0x9e377900 XOR r) au premier mot d'état.
source de référence en 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);
}
}
}
Version tweetable en C
Ce n'est peut-être pas la plus petite implémentation utilisable mais nous voulions avoir une version standard C (donc pas d'UB, et "utilisable" dans une bibliothèque).
#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);}
Vecteur de test
L'entrée suivante générée par
for (i = 0;i < 12;++i) x[i] = i * i * i + i * 0x9e3779b9;
et les valeurs "imprimées" par
for (i = 0;i < 12;++i) {
printf("%08x ",x[i])
if (i % 4 == 3) printf("\n");
}
Ainsi:
00000000 9e3779ba 3c6ef37a daa66d46
78dde724 1715611a b54cdb2e 53845566
f1bbcfc8 8ff34a5a 2e2ac522 cc624026
devrait retourner:
ba11c85a 91bad119 380ce880 d24c2c68
3eceffea 277a921c 4f73a0bd da5a9cd8
84b673f0 34e52ff7 9e2bef49 f41bb8d6
-round
au lieu de --round
signifie qu'il ne se termine jamais. La conversion --
en un tiret en n'est probablement pas suggérée dans le code :)