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 :)Réponses:
CJam (114 caractères)
Il s'agit d'un bloc anonyme (fonction): si vous voulez le nommer,
G
ajoutez-le:G
. Dans CJam, les noms attribués ne peuvent être que des lettres majuscules simples. Il y a de l'espace pour ajouter un commentairee# Gimli in CJam
et laisser des caractères dans un seul tweet.Test en ligne
Dissection
la source
C (gcc), 237 octets
J'ai probablement gagné des octets avec ma méthode de swapping, mais c'est trop mignon pour ne pas l'utiliser.
la source
unsigned
au lieu deuint32_t
(et le code de l'OP était un peu de la triche à utiliserlong
) parce que l'idée derrière le chiffrement est qu'il est très portable. (En fait, cela économise fondamentalement seulement 8 octets).gcc
un processeur Intel 32 bits ou 64 bits (et probablement beaucoup plus).C, 268 caractères (268 octets) en utilisant uint32_t
NB Étant donné que le code d'origine utilise
<stdint.h>
et tapeS
asuint32_t *
, je pense que l'utilisation delong
est une triche pour entrer dans 280 caractères au détriment de la portabilité, ce qui est la raison de l'utilisationuint32_t
en premier lieu. Si pour des raisons d'équité de comparaison, nous exigeons une utilisation cohérente deuint32_t
et la signature explicitevoid gimli(uint32_t *)
, le code d'origine est en réalité 284 caractères, et le code orlp est de 276 caractères.Cela peut être divisé en deux tweets avec des marqueurs de continuation comme
et
la source
long
dans ma version est sûre (en ce qui concerne la portabilité) car la taille minimale d'un long est de 32 bits par rapport à la norme (par opposition àint
). Les rotations dex
ety
sont effectuées avant l'incorporation danslong
l'affectation, ce qui les rend sécuritaires (car le décalage à droite sur la valeur signée dépend de CC). Le casting en revenant àuint32_t* S
) se débarrasse des bits supérieurs et nous met dans le bon état :).Java (OpenJDK 8) ,
351343339320318247 + 56 octetsJuste un port proche de 1: 1 de la référence pour commencer à jouer au golf.
Essayez-le en ligne!
la source
Integer
du tout? o_O Puisque vous n'utilisez aucuneInteger
méthode, il n'y a aucune raison de ne pas utiliserint
s ici ...s[0]^=(0x9e377900|r);
(à la toute fin) - ne pouvez-vous pas supprimer les parenthèses supplémentaires?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);}}}
. J'ai essentiellement apporté les modifications minimales nécessaires pour le compiler. Les règles de priorité de Java ne sont pas très différentes des C.JavaScript (ES6), 231 octets
Démo
Afficher l'extrait de code
la source
Assembleur x86 32 bits (112 octets)
(convention d'appel __cdecl)
Version Tweetable (encodage Base85 au format z85):
la source