В FIPS-197 ( расширенный стандарт шифрования , известный как AES) он широко используется SubBytes
, который может быть реализован как
unsigned char SubBytes(unsigned char x) {
static const unsigned char t[256] = {
0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76,
0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0,
0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15,
0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75,
0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84,
0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF,
0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8,
0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2,
0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73,
0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB,
0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79,
0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08,
0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A,
0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E,
0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF,
0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16};
return t[x];}
Эта функция не является произвольной; это обратимое отображение, состоящее из инверсии в поле Галуа с последующим аффинным преобразованием. Все подробности приведены в разделе 5.1.1 FIPS-197 или разделе 4.2.1 здесь (под немного другим названием).
Одна из проблем, связанных с реализацией в виде таблицы, заключается в том, что она открыта для так называемых атак с кэшированием .
Таким образом, ваша миссия состоит в том, чтобы разработать точную замену вышеуказанной SubBytes()
функции, которая демонстрирует поведение в постоянном времени; мы будем считать , что это тот случай , когда ничего не зависит от ввода x
от SubBytes
используется либо:
- в качестве индекса массива,
- как контроль операнда
if
,while
,for
,case
, или оператора?:
; - как любой операнд операторов
&&
,||
,!
,==
,!=
,<
,>
,<=
,>=
,*
,/
,%
; - в качестве правого операнда операторов
>>
,<<
,*=
,/=
,%=
,<<=
,>>=
.
Вход победы будет один с наименьшими затратами, полученный из числа операторов , выполненных во входном-зависимого пути передачи данных, с весом 5 для одинарных операторов -
и ~
, а также <<1
, >>1
, +1
, -1
; вес 7 для всех других операторов, сдвигов с другими счетами или добавлений / переходов других констант (приведение типов и продвижение бесплатно). В принципе, эта стоимость не изменяется при развертывании циклов (если они есть) и не зависит от ввода x
. Ответы с кратчайшим кодом после удаления пробелов и комментариев побеждают.
Я планирую обозначить запись как ответ, как только смогу, в 2013 году, UTC. Я рассмотрю ответы на языках, о которых я немного знаю, оценивая их как прямой перевод C, не оптимизированный по размеру.
Извинения за первоначальное упущение +1
и -1
в пользу привилегированных операторов, бесплатных забросов и рекламных акций, а также ранжирование по размеру. Учтите, что *
это запрещено как при одинарном, так и при умножении.
источник
Ответы:
Оценка:
940 933 926910, подход к полевой башнеСтруктура по сути такая же, как у Бояра и Перальты: уменьшить инверсию в GF (2 ^ 8) до инверсии в GF (2 ^ 4), разбить ее на линейный пролог, нелинейное тело и линейный эпилог, а затем свести их к минимуму отдельно. Я плачу некоторые штрафы за извлечение битов, но я компенсирую это за счет возможности выполнять операции параллельно (с некоторым разумным дополнением битов
fwd
). Подробнее...Фон
Как уже упоминалось в описании проблемы, S-блок состоит из инверсии в конкретной реализации поля Галуа GF (2 ^ 8) с последующим аффинным преобразованием. Если вы знаете, что они оба значат, пропустите этот раздел.
Аффинное (или линейное) преобразование - это функция,
f(x)
которая уважаетf(x + y) = f(x) + f(y)
иf(a*x) = a*f(x)
.Поле - это набор
F
элементов с двумя специальными элементами, которые мы будем называть0
и1
, и двумя операторами, которые мы будем называть+
и*
которые учитывают различные свойства. В этом разделе предполагается , чтоx
,y
иz
являются элементамиF
.F
формы абелевой группы в соответствии+
с0
тождеством: т.е.x + y
является элементомF
;x + 0 = 0 + x = x
; у каждогоx
есть соответствующий-x
такой, чтоx + (-x) = (-x) + x = 0
;x + (y + z) = (x + y) + z
; иx + y
=y + x
.F
отличные от0
образующих абелеву группу в соответствии*
с1
идентичностью.x * (y + z) = (x * y) + (x * z)
.Оказывается, существуют некоторые довольно жесткие ограничения на конечные поля:
p
простое число иk
степень) ,F\{0}
под*
является циклической; то есть есть по крайней мере один элементg
такой, что каждый элемент является степеньюg
.k
поля простого порядка. Например, GF (2 ^ 8) имеет представление в виде полиномовx
над GF (2). На самом деле обычно существует более одного представления. Рассмотримx^7 * x
в GF (2 ^ 8); он должен быть эквивалентен некоторому полиному порядка 7, но какой? Есть много вариантов, которые дают правильную структуру; AES решает сделатьx^8 = x^4 + x^3 + x + 1
(лексикографически наименьший многочлен, который работает).Итак, как мы вычисляем обратное в этом конкретном представлении GF (2 ^ 8)? Это слишком сложная задача, чтобы решать ее напрямую, поэтому нам нужно разобраться с ней.
Полевые башни: представляющие GF (2 ^ 8) в терминах GF (2 ^ 4)
Вместо того, чтобы представлять GF (2 ^ 8) полиномами из 8 членов над GF (2), мы можем представить его полиномами из 2 членов над GF (2 ^ 4). На этот раз нам нужно выбрать линейный полином для
x^2
. Предположим, мы выбралиx^2 = px + q
. Тогда(ax + b) * (cx + d) = (ad + bc + acp)x + (bd + acq)
.Легче ли найти обратное в этом представлении? Если
(cx + d) = (ax + b)^-1
мы получим одновременные уравненияad + (b + ap)c = 0
bd + (aq)c = 1
Позвольте
D = [b(b+ap) + a^2 q]
и установитьc = a * D^-1
;d = (b + ap) * D^-1
, Таким образом, мы можем сделать обратное в GF (2 ^ 8) для стоимости преобразования в GF (2 ^ 4), обратное и несколько сложений и умножений в GF (2 ^ 4) и обратное преобразование. Даже если мы сделаем обратное с помощью таблицы, мы уменьшили размер таблицы с 256 до 16.Детали реализации
Чтобы построить представление GF (4), мы можем выбрать один из трех полиномов для уменьшения
x^4
:x^4 = x + 1
x^4 = x^3 + 1
x^4 = x^3 + x^2 + x + 1
Самое важное отличие заключается в реализации умножения. Для любого из трех (которые соответствуют
poly
3, 9, f) будет работать следующее:Однако, если мы решим,
poly = 3
мы сможем справиться с переполнением намного эффективнее, потому что оно имеет хорошую структуру: двойного переполнения не существует, потому что оба входа являютсяx^6 = x^2 (x + 1)
кубическими и кубическими. Кроме того, мы можем сохранить сдвигиb
: поскольку мы оставляем переполнение до последнего,a0 x^2
не имеет никаких установленных битов, соответствующихx
или 1, и поэтому мы можем замаскировать его с -4 вместо -1. РезультатНам все еще нужно выбрать значения
p
иq
для представления GF (2 ^ 8) над GF (2 ^ 4), но у нас не так много на них ограничений. Единственное, что имеет значение, - это то, что мы можем получить линейную функцию от битов нашего исходного представления до битов рабочего представления. Это имеет значение по двум причинам: во-первых, линейные преобразования легко выполнять, тогда как нелинейное преобразование потребует оптимизации, эквивалентной по сложности простой оптимизации всего S-блока; во-вторых, потому что мы можем получить некоторые побочные выгоды. Чтобы подвести итог структуры:Если биты
x
являютсяx7 x6 ... x0
то , поскольку преобразование является линейным , мы получаемa = f({x7}0000000 + 0{x6}000000 + ... + 0000000{x0}) = f({x7}0000000) + f(0{x6}000000) + ... + f(0000000{x0})
. Возведем его в квадрат, и мы получим,a^2 = f({x7}0000000)^2 + f(0{x6}000000)^2 + ... + f(0000000{x0})^2
где перекрестные члены отменяются (потому что в GF (2),1 + 1 = 0
). Так жеa^2
может быть вычислено как линейная функцияx
. Мы можем увеличить прямое линейное преобразование, чтобы получить:и мы до трех умножений и одного сложения. Мы также можем расширить код умножения для
Dinv
параллельного выполнения двух умножений . Таким образом, наша общая стоимость - прямое линейное преобразование, сложение, два умножения, обратное в GF (2 ^ 4) и обратное линейное преобразование. Мы можем добавить пост-обратное линейное преобразование S-блока и получить его практически бесплатно.Вычисление коэффициентов для линейных преобразований не очень интересно, и при этом микрооптимизация не позволяет сохранить маску здесь и сдвиг там. Остальная интересная часть - это оптимизация
inverse_GF16
, Существует от 2 до 64 различных функций от 4 до 4 бит, поэтому прямая оптимизация требует много памяти и времени. Что я сделал, так это рассмотрел 4 функции от 4 бит до 1 бита, ограничил общую стоимость, разрешенную для какой-либо одной функции (с максимальной стоимостью 63 за функцию я могу перечислить все подходящие выражения менее чем за минуту), и для каждого набора функций делайте общее исключение подвыражения. После 25 минут хруста, я обнаружил, что наилучшее возможное обратное значение с этим ограничением имеет общую стоимость 133 (в среднем 33,25 на бит на выходе, что неплохо, учитывая, что самое дешевое выражение для любого отдельного бита - 35) ,Я все еще экспериментирую с другими подходами, чтобы минимизировать инверсию в GF (2 ^ 4), и подход, который строит восходящий, а не нисходящий, принес улучшение с 133 до 126.
источник
& 1
может быть обрезано (особенно еслиx
естьunsigned char
;CHAR_BIT
в Codegolf есть 8).Оценка: 980 = 7 * 5 + 115 * 7 + 7 * 5 + 15 * 7, минимизация Бояра и Перальты
Я нашел новую технику минимизации комбинационной логики с приложениями к криптологии Джоан Бояр и Рене Перальта, которая (за исключением формализма C) делает то, что требуется. Методика, используемая для получения их уравнений, запатентована не менее, чем в США. Я просто сделал прямой перевод их уравнений , любезно связанных здесь .
источник
Счет: 10965
При этом используется тот же принцип развертывания поиска в массиве. Может потребоваться дополнительные броски.
Спасибо Угорену за указание, как улучшить
is_zero
.источник
(c|-c)>>31
0 для 0 и -1 в противном случае.c >> 4
мне кажется, что вы подписали правильный сдвиг вправо. И если ты действительно настаиваешь -((unsigned int)(c|-c))>>31
естьc?1:0
.c >>4
Работает с или без расширения знака. Но хорошая выгода от использования беззнаковой смены: будет редактировать, когда я вернусь домой, и могу использовать подходящий компьютер, а не телефон. Благодарю.Оценка: 9109, алгебраический подход
Я оставлю подход поиска в случае, если кто-то сможет значительно улучшить его, но оказывается, что возможен хороший алгебраический подход. Эта реализация находит мультипликативное обратное с использованием алгоритма Евклида . Я написал его на Java, но в принципе его можно перенести на C - я прокомментировал части, которые могли бы измениться, если вы захотите переработать его, используя только 8-битные типы.
Спасибо Угорену за то, что он указал, как сократить
is_nonzero
проверку в комментарии к моему другому ответу.источник
Оценка: 256 * (7+ (8 * (7 + 7 + 7) - (2 + 2)) + 5 + 7 + 7) = 48640 (при условии, что циклы развернуты)
Объяснение:
По сути, поиск в массиве переопределен с использованием побитовых операторов и всегда обрабатывает весь массив. Перебираем массив, и xor
x
с каждым индексом, затем используем побитовые операторы для логического отрицания результата, поэтому мы получаем 255 когдаx=i
и 0 в противном случае. Мы поразрядно - и это со значением массива, так что выбранное значение остается неизменным, а остальные становятся равными 0. Затем мы берем побитовый - или этого массива, тем самым вытаскивая только выбранное значение.Эти две
1 << j
операции исчезнут как часть развертывания цикла, заменив их степенями от 2 до 1.источник
-(2+2)
ваш подсчет очков? Редактировать: ах, встраивание создает<<1
и>>1
.Оценка
19681692, используя справочную таблицуПримечание. Это решение не соответствует критериям из-за
w >> b
.Использование таблицы поиска, но чтение 8 байтов за раз.
3 * 7 + 32 * (6 * 7 + 2 * 5) + 7 = 692
источник
w>>b
что RHS рассчитывается изx
w >> b
гдеb
зависит от ввода; Кроме тогоx/8
,x%8
и*= (1-badi)
. Первый, скорее всего, выродится в временную зависимость от младших процессоров. Тем не менее, идея использования широких переменных, безусловно, имеет потенциал.w >> b
это очень важно (мне нужно посмотреть, можно ли это исправить, не переписывая все.Таблица поиска и маска, оценка = 256 * (5 * 7 + 1 * 5) = 10240
Использует поиск таблицы с маской, чтобы выбрать только тот результат, который мы хотим. Использует тот факт, что
j|-j
является либо отрицательным (когда i! = X), либо нулевым (когда i == x). Сдвиг создает маску «все в один» или «все в ноль», которая используется для выбора только той записи, которую мы хотим.источник