Gimli, rendilo ancora più breve?


25

Sono uno degli autori di Gimli. Abbiamo già una versione a 2 tweet (280 caratteri) in C, ma vorrei vedere quanto può essere piccola.

Gimli ( carta , sito Web ) è un'alta velocità con un design di permutazione crittografica di alto livello di sicurezza che sarà presentato alla Conferenza su hardware crittografico e sistemi integrati (CHES) 2017 (25-28 settembre).

L'obiettivo

Come al solito: rendere l'implementazione ridotta di Gimli nella lingua che preferisci.

Dovrebbe essere in grado di prendere come input 384 bit (o 48 byte o 12 unsigned int ...) e restituire (può modificare sul posto se si utilizzano i puntatori) il risultato di Gimli applicato su questi 384 bit.

È consentita la conversione di input da decimale, esadecimale, ottale o binario.

Casi d'angolo potenziali

Si presume che la codifica intera sia little-endian (ad es. Quello che probabilmente hai già).

È possibile rinominare Gimliin Gma deve essere comunque una chiamata di funzione.

Chi vince?

Questo è code-golf quindi vince la risposta più breve in byte! Ovviamente si applicano le regole standard.

Di seguito viene fornita un'implementazione di riferimento.

Nota

Sono state sollevate alcune preoccupazioni:

"hey gang, per favore attua il mio programma gratuitamente in altre lingue, quindi non devo" (grazie a @jstnthms)

La mia risposta è la seguente:

Posso facilmente farlo in Java, C #, JS, Ocaml ... È più divertente. Attualmente (il team Gimli) l'abbiamo implementato (e ottimizzato) su AVR, Cortex-M0, Cortex-M3 / M4, Neon, SSE, SSE-srolled, AVX, AVX2, VHDL e Python3. :)


Informazioni su Gimli

Lo stato

Gimli applica una sequenza di round a uno stato di 384 bit. Lo stato è rappresentato come un parallelepipedo con dimensioni 3 × 4 × 32 o, equivalentemente, come una matrice 3 × 4 di parole a 32 bit.

stato

Ogni round è una sequenza di tre operazioni:

  • uno strato non lineare, in particolare una SP-box a 96 bit applicata a ciascuna colonna;
  • in ogni secondo giro, uno strato di miscelazione lineare;
  • in ogni quarto round, un'aggiunta costante.

Lo strato non lineare.

La SP-box è composta da tre sotto-operazioni: rotazioni della prima e della seconda parola; una funzione T non lineare a 3 ingressi; e uno scambio di prima e terza parola.

SP

Lo strato lineare

Lo strato lineare è costituito da due operazioni di swap, ovvero Small-Swap e Big-Swap. Lo Small Swap si verifica ogni 4 round a partire dal 1 ° round. Il Big-Swap si verifica ogni 4 round a partire dal 3 ° round.

Lineare

Le costanti rotonde.

Ci sono 24 round in Gimli, numerati 24,23, ..., 1. Quando il numero tondo r è 24,20,16,12,8,4 XOR la ​​costante tonda (0x9e377900 XOR r) alla prima parola di stato.

inserisci qui la descrizione dell'immagine

fonte di riferimento in 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);
    }
  }
}

Versione Tweet in C

Questa potrebbe non essere la più piccola implementazione utilizzabile ma volevamo avere una versione C standard (quindi nessun UB, e "utilizzabile" in una libreria).

#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);}

Test vettoriale

Il seguente input generato da

for (i = 0;i < 12;++i) x[i] = i * i * i + i * 0x9e3779b9;

e valori "stampati" di

for (i = 0;i < 12;++i) {
  printf("%08x ",x[i])
  if (i % 4 == 3) printf("\n");
}

in tal modo:

00000000 9e3779ba 3c6ef37a daa66d46 
78dde724 1715611a b54cdb2e 53845566 
f1bbcfc8 8ff34a5a 2e2ac522 cc624026 

dovrebbe restituire:

ba11c85a 91bad119 380ce880 d24c2c68 
3eceffea 277a921c 4f73a0bd da5a9cd8 
84b673f0 34e52ff7 9e2bef49 f41bb8d6 

3
Un tweet è di 140 caratteri, non un 280
Stan Strum

1
Lo so, motivo per cui si inserisce in 2;) twitter.com/TweetGimli .
Biv

10
"hey gang, per favore, implementa il mio programma gratuitamente in altre lingue, quindi non devo"
jstnthms,

hahaha Nah l'ho già in Python e posso facilmente farlo in Java, C #, JS. È più per il divertimento. :)
Biv

5
Il codice di riferimento sul sito Web presenta un errore cruciale, -roundinvece di --roundindicare che non termina mai. La conversione --in un trattino non è probabilmente suggerita nel codice :)
orlp

Risposte:


3

CJam (114 caratteri)

{24{[4/z{[8ZT].{8\#*G8#:Mmd+}__)2*\+.^W%\[_~;&8*\~@1$|2*@@&4*].^Mf%}%z([7TGT]R=4e!=\f=(2654435608R-_4%!*^\@]e_}fR}

Questo è un blocco anonimo (funzione): se vuoi nominarlo, Gaggiungi :G. In CJam i nomi assegnati possono essere solo singole lettere maiuscole. C'è spazio per aggiungere un commento e# Gimli in CJame lasciare i personaggi in un singolo tweet.

Test online

Dissezione

{                                e# Define a block
  24{                            e# For R=0 to 23...
    [                            e#   Collect values in an array
      4/z                        e#     Transpose to columns
      {                          e#     Map over each column
        [8ZT].{8\#*G8#:Mmd+}     e#       Rotations, giving [x y z]
        __)2*\+.^W%\             e#       => [y^z x^y x^z*2] [x y z]
        [_~;&8*\~@1$|2*@@&4*].^  e#       => [x' y' z']
        Mf%                      e#       Map out any bits which overflowed
      }%
      z                          e#    Transpose to rows
      ([7TGT]R=4e!=\f=           e#    Permute first row
      (2654435608R-_4%!*^        e#    Apply round constant to first element
      \@                         e#    Put the parts in the right order
    ]e_                          e#  Finish collecting in array and flatten
  }fR
}

Per un momento sono stato gettato dal fatto che l'uscita non era in esadecimale (nel test online). :)
Biv

15

C (gcc), 237 byte

#define P(a,l)x=a;a=S[c=l>>r%4*2&3];S[c]=x;
r,c,x,y,z;G(unsigned*S){
for(r=24;r;*S^=r--%4?0:0x9e377901+r){
for(c=4;c--;*S++=z^y^8*(x&y))
x=*S<<24|*S>>8,y=S[4]<<9|S[4]>>23,z=S[8],S[8]=x^2*z^4*(y&z),S[4]=y^x^2*(x|z);
S-=4;P(*S,33)P(S[3],222)}}

Probabilmente ho guadagnato byte con il mio metodo di scambio, ma è troppo carino da non usare.


perso o guadagnato?
HyperNeutrino,

@HyperNeutrino Guadagnato, rendendomi un perdente :)
orlp

Ah ok: P ha un senso: P: P
HyperNeutrino,

Questo è sicuramente un miglioramento, ma è un po 'imbarazzante da usare unsignedinvece di uint32_t(e il codice di OP era un po' imbroglione da usare long) perché l'idea alla base del codice è che è altamente portatile. (In effetti, questo in pratica consente di risparmiare solo 8 byte).
Peter Taylor,

1
@PeterTaylor Anche se il mio codice è simile, non sto davvero competendo con il codice OP. Sto lavorando secondo le regole di PPCG, dove deve funzionare almeno con un'implementazione su una piattaforma, e lo fa con gccuna CPU Intel a 32 o 64 bit (e probabilmente molte altre).
orlp,

4

C, 268 caratteri (268 byte) usando uint32_t

NB Dato che il codice originale usa <stdint.h>e digita Scome uint32_t *, penso che l'uso di longsia un imbroglio per entrare in 280 caratteri al costo della portabilità, che è la ragione per l'utilizzo uint32_tin primo luogo. Se per equità di confronto richiediamo un uso coerente uint32_te la firma esplicita void gimli(uint32_t *), il codice originale è in realtà 284 caratteri e il codice orlp è 276 caratteri.

#include<stdint.h>
#define R(V)x=S[V],S[V]=S[V^y],S[V^y]=x,
void gimli(uint32_t*S){for(uint32_t r=24,x,y,z,*T;r--;y=72>>r%4*2&3,R(0)R(3)*S^=y&1?0x9e377901+r:0)for(T=S+4;T-->S;*T=z^y^8*(x&y),T[4]=y^x^2*(x|z),T[8]=x^2*z^4*(y&z))x=*T<<24|*T>>8,y=T[4]<<9|T[4]>>23,z=T[8];}

Questo può essere diviso in due tweet con marcatori di continuazione come

#include<stdint.h>
#define R(V)x=S[V],S[V]=S[V^y],S[V^y]=x,
void gimli(uint32_t*S){for(uint32_t r=24,x,y,z,*T;r--;y=72>>r%4*2&3,R(0)R(3)// 1

e

*S^=y&1?0x9e377901+r:0)for(T=S+4;T-->S;*T=z^y^8*(x&y),T[4]=y^x^2*(x|z),T[8]=x^2*z^4*(y&z))x=*T<<24|*T>>8,y=T[4]<<9|T[4]>>23,z=T[8];}// 2/2

L'uso di longnella mia versione è sicuro (rispetto alla portabilità) perché la dimensione minima di un long è di 32 bit rispetto allo standard (al contrario di int). Le rotazioni di xe yvengono eseguite prima del cast longnell'assegnazione, rendendole sicure (poiché lo spostamento corretto sul valore con segno dipende dal CC). Il cast quando torna indietro a uint32_t* S) elimina i bit superiori e ci mette nello stato giusto :).
Biv

2

Java (OpenJDK 8) , 351 343 339 320 318 247 + 56 byte

Solo un porto vicino 1: 1 del riferimento da cui iniziare a giocare a golf.

void f(int[]x,int y,int z){int q=x[y];x[y]=x[z];x[z]=q;}

s->{for(int r=24,c,x,y,z;r>0;--r){for(c=0;c<4;x=s[c]<<24|s[c]>>>8,y=s[4+c]<<9|s[4+c]>>>23,z=s[8+c],s[8+c]=x^z<<1^(y&z)<<2,s[4+c]=y^x^(x|z)<<1,s[c++]=z^y^(x&y)<<3);if((r&3)==2){f(s,0,2);f(s,1,3);}if((r&3)<1){f(s,0,1);f(s,2,3);s[0]^=0x9e377900|r;}}}

Provalo online!


1
Perché usare Integeraffatto? o_O Dato che non usi alcun Integermetodo, non c'è motivo di non usarli intqui ...
Olivier Grégoire,

@ OlivierGrégoire Penso solo a un mio residuo che prova Integer.divideUnsigned, ma mi sono reso conto che posso avere >>>
Roberto Graham,

s[0]^=(0x9e377900|r);(alla fine) - non puoi lasciare le parentesi extra?
Clashsoft,

Lo stesso con s[4+c]>>>(23).
Clashsoft,

1
È possibile effettuare un numero inferiore di modifiche e ottenere 300: 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);}}}. Fondamentalmente ho apportato le modifiche minime necessarie per farlo compilare. Le regole di precedenza di Java non sono molto diverse da quelle di C.
Peter Taylor,

2

JavaScript (ES6), 231 byte

s=>{for(r=25;--r;[a,b,c,d,...e]=s,s=r&1?s:r&2?[c,d,a,b,...e]:[b,a,d,c,...e],s[0]^=r&3?0:0x9e377900|r)for(c=4;c--;x=s[c]<<24|s[c]>>>8,y=s[j=c+4]<<9|s[j]>>>23,z=s[c+8],s[c+8]=x^z*2^(y&z)*4,s[j]=y^x^(x|z)*2,s[c]=z^y^(x&y)*8);return s}

dimostrazione


0

Assemblatore x86 a 32 bit (112 byte)

(__cdecl chiamata convention)

            pusha
            mov     ecx, 9E377918h
    loc_6:  mov     esi, [esp+24h]
            push    esi
            push    4
            pop     ebx
    loc_E:  lodsd
            ror     eax, 8
            mov     ebp, [esi+0Ch]
            rol     ebp, 9
            mov     edx, [esi+1Ch]
            push    eax
            push    ebp
            lea     edi, [edx+edx]
            and     ebp, edx
            shl     ebp, 2
            xor     edi, ebp
            xor     eax, edi
            mov     [esi+1Ch], eax
            pop     ebp
            pop     eax
            push    eax
            push    ebp
            xor     ebp, eax
            or      eax, edx
            shl     eax, 1
            xor     ebp, eax
            mov     [esi+0Ch], ebp
            pop     ebp
            pop     eax
            xor     edx, ebp
            and     eax, ebp
            shl     eax, 3
            xor     edx, eax
            push    edx
            dec     ebx
            jnz     short loc_E
            pop     esi
            pop     ebp
            pop     ebx
            pop     eax
            pop     edi
            mov     dl, cl
            and     dl, 3
            jnz     short loc_5B
            xchg    eax, ebx
            xchg    esi, ebp
            xor     eax, ecx
    loc_5B: cmp     dl, 2
            jnz     short loc_63
            xchg    eax, ebp
            xchg    esi, ebx
    loc_63: stosd
            xchg    eax, ebx
            stosd
            xchg    eax, ebp
            stosd
            xchg    eax, esi
            stosd
            dec     cl
            jnz     short loc_6
            popa
            retn

Versione Tweetable (codifica Base85 in formato z85):

v7vb1h> C} HbQuA91y51A:? oWYw48G) I = H /] rGf9Na> sA.DWu06 {6f # TEC ^ CM: # IEA-cstx7:!!> VfVf # u * YB & MP (tuCl * + 7eENBP) $ :) Lh k } t $ ^ wM51j% LDF $ HMAg2bB ^ MQP
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.