Требуется, чтобы компилятор выдавал код без ветвей/с постоянным временем

#c #c #gcc #clang #secure-coding

Вопрос:

В криптографии любой фрагмент кода, зависящий от секретных данных (таких как закрытый ключ), должен выполняться в постоянное время, чтобы избежать временных атак по боковым каналам.

Наиболее популярные в настоящее время архитектуры (x86-64 и ARM AArch64) поддерживают определенные виды инструкций условного выполнения, такие как:

Даже когда такие инструкции недоступны, существуют методы преобразования фрагмента кода в версию без ветвей. Производительность может пострадать, но в этом сценарии это не главная цель-работать в постоянном времени.

Поэтому в принципе должно быть возможно писать код без ветвей, например, на C/C , и действительно видно, что gcc/clang часто будет выдавать код без ветвей с включенной оптимизацией (для этого даже есть специальный флаг в gcc: -fif-conversion2 ). Тем не менее, это, по-видимому, решение об оптимизации, и если компилятор считает, что безветвие будет работать хуже (скажем, если предложения «тогда» и «еще» выполняют много вычислений, больше, чем стоимость очистки конвейера в случае неверно предсказанной ветви), то я предполагаю, что компилятор будет выдавать обычный код.

Если постоянное время является необоротной целью, можно быть вынужденным использовать некоторые из вышеупомянутых трюков для создания кода без ветвей, что делает код менее понятным. Кроме того, производительность часто является второстепенной и довольно важной целью, поэтому разработчик должен надеяться, что компилятор выведет предполагаемую операцию, лежащую в основе кода без ветвей, и выдаст эффективную последовательность команд, часто используя инструкции, упомянутые выше. Для этого может потребоваться переписывать код снова и снова, просматривая выходные данные сборки, пока волшебное заклинание не удовлетворит компиляторов-и это может меняться от компилятора к компилятору или когда выйдет новая версия.

В целом, это ужасная ситуация с обеих сторон: авторы компиляторов должны выводить намерения из запутанного кода, преобразуя его в гораздо более простую последовательность инструкций; в то время как разработчики должны писать такой запутанный код, поскольку нет никаких гарантий, что простой, понятный код действительно будет выполняться в течение постоянного времени.

Превращая это в вопрос: если определенный фрагмент кода должен быть выдан в постоянное время (или вообще не должен), существует ли флаг компилятора или прагма, которые заставят код быть выдан как таковой, даже если компилятор прогнозирует худшую производительность, чем разветвленная версия, или прервут компиляцию, если это невозможно? Разработчики смогут писать понятный код с уверенностью, что он будет постоянным по времени, предоставляя компилятору понятный и простой для анализа код. Я понимаю, что это, вероятно, вопрос, зависящий от языка и компилятора, поэтому я был бы удовлетворен ответами на C или C для gcc или clang.

Комментарии:

1. Вероятно, вам нужно самому написать ассемблерный код. Я сомневаюсь, что какой-либо компилятор общего назначения поможет вам.

2. Обратите внимание, что просто сделать код без разветвлений недостаточно-эффекты кэша могут вызвать изменения во время выполнения, которые приводят к утечке информации, и этого может быть невозможно избежать на некоторых аппаратных средствах. Для обеспечения реальной безопасности вам также необходимо тщательно разработать аппаратное обеспечение.

3. Если вы заинтересованы в безопасном выполнении кода на процессоре общего назначения, это неправильный вопрос. Вы должны спросить: «Как моя секретная информация может просочиться из этой системы и как мне это предотвратить?» При утечках времени задержка результата до определенного времени намного лучше, чем попытка написать код без ветвей, поскольку это также позволяет избежать других проблем со временем, которые могут возникнуть.

4. Я не знаком с этой техникой. Можете ли вы привести какие-либо рецензируемые работы, поддерживающие эту технику? Насколько я понимаю, код без ветвей с постоянным временем является золотым стандартом, по крайней мере, в академических кругах. Более того, я совершенно уверен, что предложенная вами методика не будет устойчива к атакам анализа мощности, когда это применимо (смарт-карты, встроенные устройства и т. Д.).

5. Вероятно, вам понадобился бы специализированный компилятор, разработанный для этой цели, вместо того, чтобы пытаться переоборудовать его в существующий компилятор, который более 30 лет развивался в направлении противоположной цели (наиболее эффективный код). Если на то пошло, C может быть неправильным языком для начала, так как его семантика затрудняет это. (Например, такой код a = cond ? *p : 0; нельзя сделать безразветвленным, потому p что допускается недопустимый указатель, когда cond значение равно false, поэтому компилятор должен убедиться, что загрузка в этом случае вообще не происходит.)