Я реализую AES256 / GCM шифрование и аутентификация с использованием библиотеки Crypto ++. Мой код скомпилирован с использованием Visual Studio 2008 как проект C ++ / MFC. Это несколько более старый проект, который использует предыдущую версию библиотеки, Cryptopp562
,
Мне любопытно, будет ли полученный скомпилированный код использовать Intel AES-NI инструкции? И если так, что произойдет, если аппаратное обеспечение (старый процессор) не поддерживает его?
РЕДАКТИРОВАТЬ: Вот пример кода, с которым я его тестирую:
int nIV_Length = 12;
int nAES_KeyLength = 32;
BYTE* iv = new BYTE[nIV_Length];
BYTE* key = new BYTE[nAES_KeyLength];
int nLnPlainText = 128;
BYTE* pDataPlainText = new BYTE[nLnPlainText];
CryptoPP::AutoSeededRandomPool rng;
rng.GenerateBlock(iv, nIV_Length);
CryptoPP::GCM<CryptoPP::AES>::Encryption enc;
enc.SetKeyWithIV(key, nAES_KeyLength, iv, nIV_Length);
BYTE* pDataOut_AES_GCM = new BYTE[nLnPlainText];
memset(pDataOut_AES_GCM, 0, nLnPlainText);
BYTE mac[16] = {0};
enc.EncryptAndAuthenticate(pDataOut_AES_GCM, mac, sizeof(mac), iv, nIV_Length, NULL, 0, pDataPlainText, nLnPlainText);
delete[] pDataPlainText;
delete[] pDataOut_AES_GCM;
delete[] key;
delete[] iv;
Если вы запускаете код, содержащий инструкции AES-NI на оборудовании x86, которое не поддерживает эти инструкции, вы должны получить недопустимые ошибки инструкций. Если код не делает что-то умное (например, смотрит на CPUID, чтобы решить, следует ли запускать оптимизированный код AES-NI, или что-то еще), это также можно использовать для определения, действительно ли используются инструкции AES-NI.
В противном случае вы всегда можете использовать отладчик и установить контрольные точки в инструкциях AES-NI, чтобы увидеть, использует ли ваш процесс эту часть кода.
В соответствии с Примечания к выпуску Crypto ++ Поддержка AES-NI была добавлена в версии 5.6.1. Глядя на исходный код версии 5.6.5 Crypto ++, если поддержка AES-NI была включена во время компиляции, то она использует проверки во время выполнения ( HasAESNI()
функция, вероятно, с использованием CPUID), чтобы решить, использовать ли эти встроенные функции. Увидеть rijndael.cpp (а также cpu.cpp для кода CPUID) в его исходном коде для деталей.
Мне любопытно, будет ли полученный скомпилированный код использовать инструкции Intel AES-NI?
В Crypto ++ 5.6.1 добавлена поддержка AES-NI и Carryless Multiplies в GCM. Используется, когда выполняются два или три условия. Во-первых, вы используете версию библиотеки с поддержкой. С домашней страницы под Новости (или ПРОЧТИ МЕНЯ):
09.08.2010 — Выпущена версия 5.6.1
- добавлена поддержка наборов команд AES-NI и CLMUL в AES и GMAC / GCM
Во-вторых, компилятор, ассемблер и компоновщик должны поддерживать инструкции. Для Crypto ++ это означает, что вы используете как минимум MSVC 2008 SP1, GCC 4.3 и Binutils 2.19. Для MSVC, если вы посмотрите на config.h
, его охраняют следующим образом (__AES__
есть для GCC и друзей тоже)
#if ... (_MSC_FULL_VER >= 150030729) ...
#define CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE 1
#else
#define CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE 0
#endif
Вы можете искать _MSC_FULL_VER
числа в Версия для Visual Studio. По иронии судьбы, я никогда не видел подобную страницу в MSDN, хотя пакеты обновления имеют значение. Вы должны перейти на китайский сайт. Например, проверенные итераторы появились в VS2005 SP1 (IIRC).
Для Linux и GCC-совместимых GNUmakefile
проверяет версию компилятора и ассемблера. Если они слишком старые, то makefile добавляет CRYPTOPP_DISABLE_AESNI
в командной строке, чтобы отключить поддержку, даже если __AES__
определено.
CRYPTOPP_DISABLE_AESNI
появляется чаще, чем вы думаете. Например, если вы загружаете OpenBSD 6.0 (текущая версия), то
CRYPTOPP_DISABLE_AESNI
будет присутствовать, потому что их ассемблер так стар. Они в основном застряли в версии своих инструментов до GPL-2 (очевидно, они не согласились с изменениями лицензии).
В-третьих, ЦП поддерживает инструкции AES и SSE4 (причина инструкций SSE4 поясняется ниже). Эти проверки выполняются во время выполнения, и вызывается интересующая функция. HasAES()
от cpu.h
(есть также HasSSE4()
):
//! \brief Determines AES-NI availability
//! \returns true if AES-NI is determined to be available, false otherwise
//! \details HasAESNI() is a runtime check performed using CPUID
inline bool HasAESNI()
{
if (!g_x86DetectionDone)
DetectX86Features();
return g_hasAESNI;
}
Предостережение о Item (3) — это библиотека, которая должна быть скомпилирована с поддержкой Item (2). Если Item (2) не включает поддержку времени компиляции, то Item (3) не может предложить поддержку времени выполнения.
Что касается Item (3) и поддержки во время выполнения, нам недавно пришлось его настроить. Кажется, что некоторые младшие процессоры Atom, такие как D2500, имеют SSE2, SSE3, SSSE3 и AES-NI, но не SSE4.1 или SSE4.2. По словам Intel ARK, это необязательная конфигурация процессора. Мы получили одно сообщение об ошибке о недопустимой инструкции SSE4 в пути кода AES-NI, поэтому нам пришлось добавить HasSSE4()
проверять. Увидеть PR 172, Проверьте поддержку SSE4 перед использованием инструкции SSE4.1.
И если так, что произойдет, если аппаратное обеспечение (старый процессор) не поддерживает его?
Ничего такого. Используется стандартная реализация CXX, а не AES с аппаратным ускорением.
Возможно, вам будет интересно узнать, что у нас также есть другое аппаратное ускорение AES, включая ARMv8 Crypto и VIA Padlock. Мы также предоставляем другое аппаратное ускорение, такое как CRC32, Carryless-Multiplies и SHA. Все они функционируют одинаково — поддержка времени компиляции переводится в поддержку времени выполнения.
(Комментарий): Я просто установил точку останова для метода DetectX86Features в cpu.cpp … и он никогда не срабатывал …
Это может быть сложно по двум причинам. Во-первых, вызовы могут быть встроены в сборки релизов, поэтому код формируется немного иначе, чем вы ожидаете.
Во-вторых, есть глобальный генератор случайных чисел, к которому GlobalRNG()
, GlobalRNG()
AES в режиме OFB. Когда инициализаторы запускаются для test.cpp
блок перевода, GlobalRNG()
создается, что вызывает DetectX86Features()
бежать очень рано (до того, как контроль входит main
).
Возможно, вам повезет с наблюдением деталей низкого уровня с WinDbg.
Стоит также упомянуть, что AES / GCM может быть ускорен путем чередования AES с GCM. Я считаю, что идея состоит в том, чтобы выполнить 4 раунда вычисления ключа AES и 1 CLMUL параллельно. Crypto ++ не пользуется этим, но OpenSSL использует эту возможность. Я не знаю, что делают Botan или mbedTLS.
Просто чтобы закончить мой вопрос, вот мои выводы.
Метод, который вилки выполнение на поддерживаемое оборудование AES-NI
инструкции, в отличие от программного обеспечения, реализованного в библиотеке Crypto ++ для моего Пример кода выше, является Rijndael::Enc::AdvancedProcessBlocks
находится в rijndael.cpp
, Это начинается так:
size_t Rijndael::Enc::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags) const
{
#if CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE
if (HasAESNI())
return AESNI_AdvancedProcessBlocks(AESNI_Enc_Block, AESNI_Enc_4_Blocks, (MAYBE_CONST __m128i *)(const void *)m_key.begin(), m_rounds, inBlocks, xorBlocks, outBlocks, length, flags);
#endif
CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE
Переменная препроцессора будет определена, если вы собираете библиотеку Crypto ++ по крайней мере Visual Studio 2008 with SP1
(Обратите внимание, что SP1
важно.) Такая зависимость необходима, чтобы иметь возможность использовать AES-NI
внутренние (такие как _mm_aesenc_si128 а также _mm_aesenclast_si128) для создания Intel AES-NI
инструкции машинного кода.
Таким образом, добавив точку останова в начале
позволит вам отладить его прямо из Visual Studio. Внешний отладчик не требуется.
Если вы затем вступите в AESNI_AdvancedProcessBlocks
Метод фактического шифрования AES будет обработан в одном из AESNI_Enc_*
методы. Вот как актуально aesenc
а также aesenclast
машинные инструкции могут выглядеть как x86
конфигурация в Release
построить:
Таким образом, чтобы ответить на мой оригинальный вопрос, чтобы образец кода в моем посте выше мог использовать Intel AES-NI
инструкции, необходимые для создания примера кода и библиотеки Crypto ++, по крайней мере, Visual Studio 2008 with SP1
, (Просто строю это с Visual Studio 2008
или более ранняя версия, не буду сделать работу, даже если процессор, на котором работает код, поддерживает AES-NI
инструкции.) После этого никакие другие шаги не кажутся необходимыми. Библиотека обнаружит присутствие AES-NI
инструкции автоматически (HasAESNI()
функция) и будет использовать их при наличии. В противном случае по умолчанию будет реализована программная реализация.
Наконец, просто из любопытства я решил посмотреть, насколько сильно будет отличаться скорость аппаратного обеспечения от программного AES-GCM. Я использовал следующий фрагмент кода (из моего примера кода выше):
int nCntTest = 100000;
DWORD dwmsIniTicks = ::GetTickCount();
for(int i = 0; i < nCntTest; i++)
{
enc.EncryptAndAuthenticate(pDataOut_AES_GCM, mac, sizeof(mac), iv, nIV_Length, NULL, 0, pDataPlainText, nLnPlainText);
}
DWORD dwmsElapsed = ::GetTickCount() - dwmsIniTicks;
bool bHaveHwAES_Support = false;
#if CRYPTOPP_BOOL_AESNI_INTRINSICS_AVAILABLE
bHaveHwAES_Support = CryptoPP::HasAESNI();
#endif
_tprintf(L"\nTimed %d AES256-GCM encryptions %s hardware encryption of %d bytes: %u ms\n",
nCntTest, bHaveHwAES_Support ? L"with" : L"without",
nLnRealPlainText, dwmsElapsed);
Вот два результата:
а также
Это явно не всеобъемлющий тест. Я запустил его на своем рабочем столе с "Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz"
ЦПУ.
Но хорошие новости в том, что AES-GCM
шифрование кажется очень быстрым, даже без аппаратной поддержки AES.