I'm one of the authors of Gimli. We already have a 2-tweet (280 chars) version in C but I would like to see how small it can get.
Gimli (paper,website) is a high speed with high security level cryptographic permutation design that will be presented at the Conference on Cryptographic Hardware and Embedded Systems (CHES) 2017 (September 25-28).
The task
As usual: to make the smalled usable implementation of Gimli in the language of your choice.
It should be able to take as input 384 bits (or 48 bytes, or 12 unsigned int...) and return (may modify in place if you use pointers) the result of Gimli applied on these 384 bits.
Input conversion from decimal, hexadecimal, octal or binary is allowed.
Potential corner cases
Integer encoding is assumed to be little-endian (e.g. what you probably already have).
You may rename Gimli
into G
but it must still be a function call.
Who wins?
This is code-golf so the shortest answer in bytes wins! Standard rules apply of course.
A reference implementation is provided below.
Note
Some concern has been raised:
"hey gang, please implement my program for free in other languages so I don't have to" (thx to @jstnthms)
My answer is as follow:
I can easily do it in Java, C#, JS, Ocaml... It is more for the fun. Currently We (the Gimli team) have it implemented (and optimized) on AVR, Cortex-M0, Cortex-M3/M4, Neon, SSE, SSE-unrolled, AVX, AVX2, VHDL and Python3. :)
About Gimli
The state
Gimli applies a sequence of rounds to a 384-bit state. The state is represented as a parallelepiped with dimensions 3×4×32 or, equivalently, as a 3×4 matrix of 32-bit words.
Each round is a sequence of three operations:
- a non-linear layer, specifically a 96-bit SP-box applied to each column;
- in every second round, a linear mixing layer;
- in every fourth round, a constant addition.
The non-linear layer.
The SP-box consists of three sub-operations: rotations of the first and second words; a 3-input nonlinear T-function; and a swap of the first and third words.
The linear layer.
The linear layer consists of two swap operations, namely Small-Swap and Big-Swap. Small-Swap occurs every 4 rounds starting from the 1st round. Big-Swap occurs every 4 rounds starting from the 3rd round.
The round constants.
There are 24 rounds in Gimli, numbered 24,23,...,1. When the round number r is 24,20,16,12,8,4 we XOR the round constant (0x9e377900 XOR r) to the first state word.
reference source 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);
}
}
}
Tweetable version in C
This might not be the smallest usable implementation but we wanted to have a C standard version (thus no UB, and "usable" in a library).
#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 vector
The following input generated by
for (i = 0;i < 12;++i) x[i] = i * i * i + i * 0x9e3779b9;
and "printed" values by
for (i = 0;i < 12;++i) {
printf("%08x ",x[i])
if (i % 4 == 3) printf("\n");
}
thus:
00000000 9e3779ba 3c6ef37a daa66d46
78dde724 1715611a b54cdb2e 53845566
f1bbcfc8 8ff34a5a 2e2ac522 cc624026
should return:
ba11c85a 91bad119 380ce880 d24c2c68
3eceffea 277a921c 4f73a0bd da5a9cd8
84b673f0 34e52ff7 9e2bef49 f41bb8d6
-round
instead of--round
means that it never terminates. Converting--
to an en dash is probably not suggested in code :) \$\endgroup\$