Реалізуйте S-box Rijndael


15

S-box Rijndael - це часто використовувана операція при шифруванні та дешифруванні AES . Зазвичай він реалізується у вигляді 256-байтної таблиці пошуку. Це швидко, але означає, що вам потрібно перерахувати 256-байтну таблицю пошуку у своєму коді. Надіюсь, хтось із цієї натовпу міг би зробити це з меншим кодом, враховуючи основні математичні структури.

Напишіть функцію на улюбленій мові, яка реалізує S-box Rijndael. Найкоротший код виграє.


1
Бонусні бали (надходження від мене), якщо отримана функція є постійним часом (тобто немає кодових шляхів, що залежать від даних, або доступу до масиву, або будь-яка ваша мова підтримує).
Paŭlo Ebermann

@ Доступ до масиву PaŭloEbermann - це постійний час у багатьох мовах (це додає (масштабоване) значення вказівнику і відкидає його, тому таблиця пошуку настільки швидка)
грохот freak

@ratchetfreak Доступ до масиву є O (1), але фактичний час доступу залежить, наприклад, від ударів кешів або пропусків кешу, що призводить до атак бічних каналів на AES.
Paŭlo Ebermann

@ PaŭloEbermann, але ви можете скористатися скороченим кодом для заповнення таблиці пошуку, яка потім добре впишеться під сторінку пам'яті.
Пітер Тейлор

@ PaŭloEbermann і якщо 256-довжина таблиця зберігається уздовж коду (як перерахунок, сформований під час компіляції), ви майже гарантували хіт кешу
щурячий фрік

Відповіді:


6

Рубі, 161 персонаж

R=0..255
S=R.map{|t|t=b=R.select{|y|x=t;z=0;8.times{z^=y*(x&1);x/=2;y*=2};r=283<<8;8.times{r/=2;z^r<z/2&&z^=r};z==1}[0]||0;4.times{|r|t^=b<<1+r^b>>4+r};t&255^99}

Щоб перевірити вихід, ви можете скористатися наступним кодом, щоб надрукувати його в табличній формі:

S.map{|x|"%02x"%x}.each_slice(16){|l|puts l*' '}

7

GolfScript, 60 символів

{[[0 1{.283{1$2*.255>@*^}:r~^}255*].@?~)={257r}4*99]{^}*}:S;

Цей код визначає функцію з назвою, Sяка бере байт і застосовує до неї S-поле Rijndael. (Також використовується внутрішня допоміжна функція, імені rдля збереження кількох символів.)

Ця реалізація використовує таблицю логарифмів для обчислення обертів GF (2 8 ), як це запропонував Томас Порнін . Щоб зберегти кілька символів, вся таблиця логарифмів перераховується на кожен вхідний байт; тим не менше, і незважаючи на те, що GolfScript взагалі дуже повільна мова, цей код займає лише близько 10 мс для обробки байта на моєму старому ноутбуці. Попередній розрахунок таблиці логарифмів (як L) прискорює його до приблизно 0,5 мс на байт, за скромної вартості ще трьох символів:

[0 1{.283{1$2*.255>@*^}:r~^}255*]:L;{[L?~)L={257r}4*99]{^}*}:S;

Для зручності ось простий тестовий джгут, який викликає функцію S, визначену вище, для обчислення та виведення всього S-вікна в шістнадцятковій формі, як у Вікіпедії :

"0123456789abcdef"1/:h; 256, {S .16/h= \16%h= " "++ }% 16/ n*

Спробуйте цей код в Інтернеті.

(Демонстрація в Інтернеті заздалегідь обчислює таблицю логарифмів, щоб уникнути занадто багато часу. Навіть так, веб-сайт GolfScript іноді може випадково вичерпатися; це відома проблема із сайтом, і перезавантаження зазвичай виправляє його.)

Пояснення:

Почнемо з розрахунку таблиці логарифмів, а конкретно з хелперної функції r:

{1$2*.255>@*^}:r

Ця функція займає два входи в стек: байт і скорочувальна бітова маска (константа між 256 і 511). Він дублює вхідний байт, помножує копію на 2 і, якщо результат перевищує 255, XOR розміщує її за допомогою бітової маски, щоб повернути її назад під 256.

У коді генеруючого коду журнальної таблиці функція rвикликається бітовою маскою скорочення 283 = 0x11b (що відповідає поліному скорочення GF Rijndael GF (2 8 ) x 8 + x 4 + x 3 + x + 1), а результат XORed з початковим байтом, ефективно помноживши його на 3 (= x + 1, як многочлен) у кінцевому полі Ріндгаеля. Це множення повторюється 255 разів, починаючи з байта 1, і результати (плюс початковий нульовий байт) збираються в масив 257 елементів, Lякий виглядає приблизно так (середня частина опущена):

[0 1 3 5 15 17 51 85 255 26 46 ... 180 199 82 246 1]

Причина, чому існує 257 елементів, полягає в тому, що, маючи попередньо встановлений 0 і 1, що виникає двічі, ми можемо знайти модульну інверсію будь-якого заданого байта, просто переглянувши його (нульовий) індекс у цьому масиві, відкинувши його та переглянувши вгору байт на індекс заперечення в тому ж масиві. (У GolfScript, як і у багатьох інших мовах програмування, індекси негативного масиву відлічуються назад від кінця масиву.) Дійсно, саме це робить код L?~)L=на початку функції S.

Решта коду викликає функцію помічника rчотири рази за допомогою зменшення бітової маски 257 = 2 8 + 1 для створення чотирьох бітових повернених копій інвертованого вхідного байта. Всі вони збираються в масив разом з постійною 99 = 0x63 і XORed разом, щоб отримати кінцевий результат.


7

x86-64 Код машини - 23 22 20 19 байт

Використовується набір інструкцій AES-NI

66 0F 6E C1          movd        xmm0,ecx
66 0F 38 DD C1       aesenclast  xmm0,xmm1
0F 57 C1             xorps       xmm0,xmm1  
66 0F 3A 14 C0 00    pextrb      eax,xmm0,0
C3                   ret

Використовуючи конвенції для виклику Windows, приймає байт і видає байт. Не слід перевертати значення ShiftRowsтому, що це не впливає на перший байт.


2
Один раз x86_64 витягує математику і має для цього вбудований.
moonheart08

6

Таблицю можна створити без обчислення обертів у кінцевому полі GF (256), використовуючи логарифми. Це виглядатиме так (код Java, intщоб уникнути проблем з підписаним byteтипом):

int[] t = new int[256];
for (int i = 0, x = 1; i < 256; i ++) {
    t[i] = x;
    x ^= (x << 1) ^ ((x >>> 7) * 0x11B);
}
int[] S = new int[256];
S[0] = 0x63;
for (int i = 0; i < 255; i ++) {
    int x = t[255 - i];
    x |= x << 8;
    x ^= (x >> 4) ^ (x >> 5) ^ (x >> 6) ^ (x >> 7);
    S[t[i]] = (x ^ 0x63) & 0xFF;
}

Ідея полягає в тому, що 3 - це мультиплікативний генератор GF (256) *. Таблиця t[]така, що t[x]дорівнює 3 х ; оскільки 3 255 = 1, отримуємо, що 1 / (3 x ) = 3 255-x .


чи не повинно бути 0x1B(один 1 у шістнадцятковій літералі) замість0x11B
храповик урод

@ratchetfreak: ні, це має бути 0x11B (я спробував). intТип 32-біт в Java; Я повинен скасувати вищий біт.
Томас Порнін

ах не здогадувався про це
грохот урод

Це >>> замість >> у рядку 4?
Джо З.

@JoeZeng: і те, і інше працювали б. У Java ">>>" - це "непідписаний зсув", ">>" - це "підписаний зсув". Вони відрізняються тим, як обробляють біт знака. Тут значення ніколи не будуть досить широкими, щоб біт знака був не нульовим, тому це не має ніякої реальної різниці.
Томас Порнін

6

GolfScript (82 символи)

{256:B,{0\2${@1$3$1&*^@2/@2*.B/283*^}8*;;1=},\+0=B)*:A.2*^4A*^8A*^128/A^99^B(&}:S;

Використовує глобальні змінні Aта Bі створює функцію як глобальну змінну S.

Інверсія Галуа - груба сила; Я експериментував з окремою mulфункцією, яку можна було б повторно використати для афінної трансформації після інверсії, але вона виявилася дорожчою через різну поведінку переповнення.

Це занадто повільно для демонстрації в Інтернеті - це б очікувало час очікування навіть у перших двох рядках таблиці.


Моє швидше (і коротше;). Але все одно +1.
Ільмарі Каронен

4

Пітон, 176 символів

Ця відповідь стосується коментаря Паола Ебермана щодо того, щоб функція була постійною в часі. Цей код підходить до рахунку.

def S(x):
 i=0
 for y in range(256):
  p,a,b=0,x,y
  for j in range(8):p^=b%2*a;a*=2;a^=a/256*283;b/=2
  m=(p^1)-1>>8;i=y&m|i&~m
 i|=i*256;return(i^i/16^i/32^i/64^i/128^99)&255

Постійне множення залежить від платформи (навіть на 32-бітних платформах, наприклад, ARM Cortex M0). Дивіться це пов’язане питання
fgrieu

1
@fgrieu Звичайно, але це все множення на константи, які можна легко реалізувати за постійний час, використовуючи зрушення та додавання.
Кіт Рендалл

2

г

ubyte[256] getLookup(){

    ubyte[256] t=void;
    foreach(i;0..256){
        t[i] = x;
        x ^= (x << 1) ^ ((x >>> 7) * 0x1B);
    }
    ubyte[256] S=void;
    S[0] = 0x63;
    foreach(i;0..255){
        int x = t[255 - i];
        x |= x << 8;
        x ^= (x >> 4) ^ (x >> 5) ^ (x >> 6) ^ (x >> 7);
        S[t[i]] = cast(ubyte)(x & 0xFF) ^ 0x63 ;
    }
    return S;

}

це може створити таблицю пошуку під час компіляції, я міг би зберегти деякі, зробивши ubyte загальним парам

редагувати не направляти ubyteна ubyteбез пошуку в масиві, що не розгалуження і повністю unrollable петлі

B[256] S(B:ubyte)(B i){
    B mulInv(B x){
        B r;
        foreach(i;0..256){
            B p=0,h,a=i,b=x;
            foreach(c;0..8){
                p^=(b&1)*a;
                h=a>>>7;
                a<<=1;
                a^=h*0x1b;//h is 0 or 1
                b>>=1;
            }
            if(p==1)r=i;//happens 1 or less times over 256 iterations
        }
        return r;
    }
    B s= x=mulInv(i);
    foreach(j,0..4){
        x^=(s=s<<1|(s>>>7));
    }
    return x^99;
}

edit2 використовував альго @Thomas для створення таблиці пошуку


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.