Я один из авторов Гимли. У нас уже есть версия с 2 твитами (280 символов) в C, но я бы хотел посмотреть, насколько она может быть маленькой.
Gimli ( статья , веб-сайт ) - это высокоскоростной дизайн криптографической перестановки с высоким уровнем безопасности, который будет представлен на Конференции по криптографическому оборудованию и встраиваемым системам (CHES) 2017 (25-28 сентября).
Задание
Как обычно: сделать небольшую полезную реализацию Gimli на выбранном вами языке.
Он должен иметь возможность принимать в качестве входных данных 384 бита (или 48 байт, или 12 беззнаковых целых ...) и возвращать (может изменить на месте, если вы используете указатели) результат Gimli, примененный к этим 384 битам.
Допускается преобразование входных данных из десятичного, шестнадцатеричного, восьмеричного или двоичного.
Потенциальные угловые случаи
Предполагается, что целочисленное кодирование является прямым порядком байтов (например, то, что вы, вероятно, уже имеете).
Вы можете переименовать Gimli
в, G
но это все равно должен быть вызов функции.
Кто выигрывает?
Это код-гольф, поэтому выигрывает самый короткий ответ в байтах! Стандартные правила применяются конечно.
Ссылочная реализация приведена ниже.
Заметка
Некоторая обеспокоенность была поднята:
«Эй, банда, пожалуйста, реализуй мою программу бесплатно на других языках, чтобы мне не пришлось» (спасибо @jstnthms)
Мой ответ следующий:
Я легко могу сделать это на Java, C #, JS, Ocaml ... Это больше для удовольствия. В настоящее время мы (команда Gimli) внедрили (и оптимизировали) это для AVR, Cortex-M0, Cortex-M3 / M4, Neon, SSE, развернутых SSE, AVX, AVX2, VHDL и Python3. :)
О Гимли
Штат
Гимли применяет последовательность раундов к 384-битному состоянию. Состояние представлено в виде параллелепипеда с размерами 3 × 4 × 32 или, что эквивалентно, в виде матрицы 3 × 4 из 32-разрядных слов.
Каждый раунд представляет собой последовательность из трех операций:
- нелинейный уровень, в частности 96-битный SP-блок, применяемый к каждому столбцу;
- в каждом втором раунде слой линейного смешивания;
- в каждом четвертом раунде постоянное прибавление.
Нелинейный слой.
SP-бокс состоит из трех подопераций: повороты первого и второго слов; нелинейная Т-функция с 3 входами; и обмен первым и третьим словами.
Линейный слой.
Линейный слой состоит из двух операций подкачки, а именно, Small-Swap и Big-Swap. Мелкий обмен происходит каждые 4 раунда, начиная с 1-го раунда. Большой своп происходит каждые 4 раунда, начиная с 3-го раунда.
Круглые константы.
В Гимли 24 раунда, пронумерованных 24,23, ..., 1. Когда число раунда r равно 24,20,16,12,8,4, мы XOR округляем константу (0x9e377900 XOR r) до первого слова состояния.
справочный источник в 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 версия в C
Это может быть не наименьшая используемая реализация, но мы хотели иметь стандартную версию C (таким образом, нет UB и «можно использовать» в библиотеке).
#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);}
Тестовый вектор
Следующий вход, сгенерированный
for (i = 0;i < 12;++i) x[i] = i * i * i + i * 0x9e3779b9;
и «напечатанные» значения по
for (i = 0;i < 12;++i) {
printf("%08x ",x[i])
if (i % 4 == 3) printf("\n");
}
таким образом:
00000000 9e3779ba 3c6ef37a daa66d46
78dde724 1715611a b54cdb2e 53845566
f1bbcfc8 8ff34a5a 2e2ac522 cc624026
должен вернуть:
ba11c85a 91bad119 380ce880 d24c2c68
3eceffea 277a921c 4f73a0bd da5a9cd8
84b673f0 34e52ff7 9e2bef49 f41bb8d6