УДК 003.26, 004.056.55, 004.021
ОБ ОПТИМИЗАЦИИ КРИПТОГРАФИЧЕСКИХ АЛГОРИТМОВ ПОСРЕДСТВОМ АССЕМБЛЕРНЫХ ВСТАВОК ПРИ ЦЕЛОЧИСЛЕННОМ ДЕЛЕНИИ
Ю.В. Гольчевский, П.А. Северин
Рассмотрен подход к ускорению криптографических алгоритмов при помощи ассемблерных вставок на примере предложенного алгоритма вычисления остатка при целочисленном делении.
Ключевые слова: программирование, оптимизация, криптография, ускорение криптографических вычислений, ассемблер, приведение по модулю.
Для криптографических приложений, работа которых связана с целыми числами многократной точности, одной из наиболее важных проблем является достижение приемлемой скорости выполнения.
Своеобразной «нишей» для оптимизации является интенсивное развитие аппаратных составляющих современных вычислительных комплексов, опережающее возможности интегрированных средств разработки, поскольку даже разработчики фирменных компиляторов не в состоянии оптимизировать их под любое конкретное вычислительное устройство, таким образом, задача получения кода, максимально эффективного для конкретного вычислительного средства, остается нерешенной.
Использование вышеуказанной оптимизационной ниши более подробно обсуждается в [1]. Однако даже если исключить из рассмотрения специфичные и малораспространенные архитектуры, и спроецировать данный подход на вычислительные комплексы, представляющие собой обычные пользовательские автоматизированные рабочие места (АРМ) архитектуры х86, можно обнаружить существенный оптимизационный ресурс.
Основными составляющими данного оптимизационного ресурса являются использование определенных ограничений на длину обрабатываемых целых чисел многократной точности, применение возможностей оптимизации, осуществляемой при помощи ассемблерных вставок, а также тот факт, что для современных пользовательских АРМ ограничение на объем памяти, занимаемой программой, не столь существенно, как на начальных этапах развития компьютерной техники.
Рассмотрим совокупность указанных методов применительно к реализации алгоритма взятия остатка по модулю, принимающего в качестве входных данных два целых числа многократной точности, длины которых кратны (для примера) 512 бит. Старший бит делителя всегда равен 1, что дает возможность упростить обычно используемый для вычисления остатка алгоритм, описание которого можно найти в [2].
Пусть неотрицательные целые числа а и Ь заданы в системе счисле-
295
ния с основанием В, т > п, Ьп > [В /2] (квадратные скобки здесь и далее обозначают целую часть заключенного в них числа):
а = (От, ®т—1, • • •, а^) = ОтВт 1 + ®т—\Вт 2 + к + а2В + а^,
Ь = (Ьт, Ьт—1,к, Ь1) = ЬтВт 1 + Ьт—1ВШ 2 + ••• + Ь2В + Ь1.
Аппроксимация q = [(атВ + ат—1 )(Ьп +1)—1 ] является нижней оценкой частного [а / Ь], так как (с учетом
— Вп—1 + Ьп—1Вп—2 + • + Ь1 < —Вп—1 + Вп—1 — 1 < —1):
а > атВ + ат—1 о
а
>
Ь
Ь
Ьп +1
атВ + ат—1 Ьп +1
О
>т—4
О вт—2 + ат—3В'" 4 + к + а1 > вп—1 + — Вп 1 + Ьп—іВП 2 + ... + Ьі
атВ + ат—1 При выполнении условия ат < Ьп
атВ + ат—1
Ьп +1
Ьп +1
< В О В(Ьп — ап) + В — ат—1 > 0.
То есть полученная оценка частного строго меньше В. Таким обра-
32
зом, для ее вычисления при В = 2 можно использовать деление 64разрядного числа на 32-разрядное без опасения переполнения (команда div архитектуры х86). Для выполнения условия перед началом деления в случае ат > Ьп выполняется присваивание
а = а — ЬВ
т-п
(1)
Так как вычитается число, кратное Ь, то значение остатка от деления на Ь не изменяется.
Можно показать, что а/(ЬВт—п—1) — q < 3, то есть для коррекции
результата после выполнения присваивания а = а — qЬBm—п—1 потребуется не более двух вычислений (1):
а ( атВ + ат—1 ^ атВ + ат—1В
ЬВ
т—п—1
< атВ + ат—1 + 1
V
т
Ьп +1
<
т—2
+В
т—2
1
1
ЬпВт—2
В +1 В +1 „ 2 „
<-----<-------< 2 + —< 3
Ьп В/2 В
ат В + ат -1
__________т-
Ьп +1 у
<
Ьп (Ьп +1) Ьп
При этом учтено, что минимальное значение Ьп равно [В /2] (старший бит равен 1).
Обобщение приведенных выше рассуждений позволяет сформулировать алгоритм.
Вход: целые числа многократной точности а и b (из m и n слов).
Выход: остаток от деления a на b.
1. Пока m > n выполнять:
1.1. Если a > bBm-n, то a = a - bBm-n,
1.2. Если am Ф 0, то a = a - [(amB + am-i)(bn +1)-1]bBm-n-1,
1.3. Если am Ф 0, то a = a - bBm-n-1,
1.4. Если am Ф 0, то a = a - bBm-n-1,
1.5. m = m -1.
2. Если a > b, то a = a - b.
3. Вернуть а.
Его особенностью является то, что в случае bn = B -1 операция деления неприменима, поскольку bn +1 = 0 . Однако значение q вычисляется и без деления, поскольку в этом случае q = am, что должно быть учтено в соответствующей функции деления, выполняемой на шаге 1.2.
Ограничение на равенство 1 старшего бита делителя не является существенно снижающим универсальность алгоритма, поскольку в случае криптографических вычислений осуществляется приведение по модулю, который, как правило, является простым числом (или произведением простых чисел), поэтому воспользовавшись соответсвующим свойством операции приведения по модулю, можно исскуственно «увеличить» делитель путем побитового сдвига на требуемое число разрядов, выполнить указанный выше алгоритм, а затем выполнить над его выходом одно финальное корректирующее деление, которое может быть целиком реализовано на языке высокого уровня.
Как можно заметить, наиболее часто встречающимися операциями в приведенном алгоритме являются операции, реализующие арифметические примитивы a = a - b и s = s - bd, где d - 32-битное число.
Данные функции целесообразно реализовать с использованием ассемблерных вставок, воспользовавшись простым приемом развертывания циклов.
Рассмотрим код оптимизированной по скорости функции dif(), осуществляющей присваивание a = a - b для чисел одинаковой длины, кратной 512 битам, принимающей указатели на массивы, хранящие значения данных чисел:
void dif(unsigned long *а, unsigned long *b)
{_asm{
mov ecx,ntotal mov eax,a
mov ebx,b clc stt:
mov edx,[ebx] sbb [eax],edx mov edx,[ebx+4] sbb [eax+4],edx
mov edx,[ebx+60] sbb [eax+60],edx lea ecx,[ecx-16] jecxz stp lea eax,[eax+64] lea ebx,[ebx+64] jmp stt stp:
sbb [eax+64],ecx }}
Количество слов в умножаемом числе находится в переменной ntotal. Для хранения результата переноса использован флаг переноса СF процессора (до начала вычислений установленный в ноль — строка 6), обработку которого осуществляет команда sbb. Знак многоточия в строке 12 использован, чтобы не приводить полную развертку кода на листинге, поскольку код представляет собой незначительно измененную периодически повторяющуюся последовательность инструкций sbb и mov его создание легко осуществить, например, с помощью несложного командного файла (результат находится в t.txt):
for /L %%i in (0,4,60) do
(echo mov edx,[ebx+%%i]>>t.txt)&&
(echo sbb [eax+%%i],edx>>t.txt
Функция sml() является вспомогательной для функции вычисления остатка и для 32-битного числа d и чисел b длинна которого кратна 512 бит и s (на 32 бита больше b) вычисляет s = s - bd за один проход:
void sml(unsigned long *a,unsigned long *b,unsigned long d)
{_asm{ mov esi,b mov edi,a mov ecx,ntotal mov ebx,d mov ebpspace,ebp mov espspace,esp mov esp,ebx xor ebx,ebx xor edx,edx
stt:
mov eax,[esi] mov ebp,edx mul esp add eax,ebp adc edx,0 shr bl,1 sbb [edi],eax rcl bl,1
add edi,64
sub ecx,16
jecxz end
add esi,64
jmp stt
end:
shr bl,1
sbb [edi],edx
mov ebp,ebpspace
mov esp,espspace}}
Операции со стеком хотя и удобны, но не обладают высоким быстродействием. Более оптимальным является использование самих стековых регистров для арифметических операций (их значения восстанавливаются с помощью соответствующих глобальных переменных). Добавление бита переноса в строке 17 не приводит к переполнению edx, так как его максимальное значение — FFFFFFFE (старшее слово квадрата числа FFFFFFFF максимального значения 32-разрядного регистра).
Таким образом, следуя [1], программную реализацию алгоритма вычисления остатка для целых чисел большой точности можно представить в виде двух абстрактных уровней:
1) созданный на языке высокого уровня, реализующий цикл алгоритма, приведенного на первом листинге, и другие некритичные с точки зрения вспомогательные алгоритмы;
2) уровень функций, реализованных с помощью ассемблерных вставок и выполняющих наиболее ресурсоемкие операции.
Данный подход позволил добиться увеличенной производительности алгоритма в целом (по разным оценкам не менее, чем на порядок по сравнению с реализацией на языке высокого уровня), не переписывая ее на ассемблере и не усложняя программный код процедурами специальных методов (арифметика Монтгомери, умножение Карацубы), хотя возможность их применения для дальнейшей оптимизации имеет место.
Необходимо отметить, что ускорение достигается в основном за счет общих приемов (развертывание циклов, минимизация обращений к
памяти, экономная работа с флагами переноса и т. п.) Поэтому данный метод ускорения обладает ограниченной переносимостью и применим не только для рассматриваемой архитектуры х86, так как использует ограниченное количество специфических команд, эквивалентные которым можно найти и в других архитектурах.
Однако это и является его основным недостатком, поскольку без использования специфических команд получение кода, максимально эффективного для конкретного вычислительного средства, остается невозможным, кроме того, оптимизация под конкретную архитектуру с учетом значительного (десятки, а порой и сотни команд) набора инструкций потребует от разработчиков значительных усилий.
В данном случае перспективным является использование технологий супероптимизации, аналогичных указанным в [3]. При этом необходимо подчеркнуть, что на данном этапе супероптимизации успешно подвергаются не содержащие циклов, ограниченные по числу инструкций участки кода, которые являются прямыми аналогами использованных при развертке циклов, как показано выше на примере кодов функций dif() (строки 8-11) и sml() (строки 13-20).
Список литературы
1. Северин П.А., Гольчевский Ю.В. Комплексный подход к ускорению криптографических вычислений // Информационные технологии в управлении и экономике [Электронный ресурс] URL: http://itue.ru/?p=286 (дата обращения 17.02.2013).
2. Кнут Д. Искусство программирования. Т. 2. Получисленные алгоритмы. 3-е изд. М.: Издательский дом «Вильямс», 2001. 832 с.
3. Eric Schkufza, Rahul Sharma, Alex Aiken. Stochastic Superoptimization. [Электронный ресурс] URL: http://arxiv.org/abs/1211.0557 (дата обращения 17.02.2013).
Гольчевский Юрий Валентинович, канд. физ.-мат. наук., доц., [email protected]. Россия, Сыктывкар, Сыктывкарский государственный университет,
Северин Павел Алексеевич, магистрант, [email protected]. Россия, Сыктывкар, Сыктывкарский государственный университет
CRYPTOGRAPHIC ALGORITHMS OPTIMIZATION BY MEANS OF ASSEMBLY INSERTS
IN INTEGER DIVISION
Yu.V. Golchevskiy, P.A. Severin
An approach to accelerate cryptographic algorithms by means of assembly inserts in the algorithms of calculation of the rest at integer division is considered.
Key words: programming, optimization, cryptography, acceleration of cryptographic calculations, the assembler, reduction on the module.