我使用Crypto ++库实现AES256/GCM encryption和身份验证。我的代码使用Visual Studio 2008编译为C ++ / MFC项目。这是一个较旧的项目,它使用以前版本的库Cryptopp562
。
我很好奇,如果生成的编译代码将使用英特尔AES-NI instructions?如果是这样,如果硬件(较旧的CPU)不支持它会发生什么?
编辑: 以下是我测试的代码示例:
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;
答案 0 :(得分:2)
如果在不支持这些指令的x86硬件上运行包含AES-NI指令的代码,则应该得到无效的指令错误。除非代码执行智能操作(例如查看CPUID以决定是否运行AES-NI优化代码或其他内容),否则这也可用于检测是否实际使用了AES-NI指令。
否则,您始终可以使用调试器,并在AES-NI指令处设置断点,以查看您的进程是否曾使用过该部分代码。
根据Crypto++ release notes AES-NI支持版本5.6.1中添加了。查看5.6.5版Crypto ++的源代码,如果在编译时启用了AES-NI支持,那么它使用运行时检查(HasAESNI()
函数,可能利用CPUID)来决定是否使用这些内在函数。有关详细信息,请参阅其源代码中的rijndael.cpp(以及cpu.cpp中的CPUID代码)。
答案 1 :(得分:1)
我很好奇所得到的编译代码是否会使用英特尔的AES-NI指令?
Crypto ++ 5.6.1在GCM下增加了对AES-NI和Carryless Multiplies的支持。当满足两个或三个条件时使用它。首先,您使用的是支持的库版本。来自 新闻 (或自述文件)下的主页:
2010年8月9日 - 版本5.6.1已发布
- 在AES和GMAC / GCM中添加了对AES-NI和CLMUL指令集的支持
其次,编译器,汇编器和链接器必须支持这些指令。对于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 version个号码。具有讽刺意味的是,即使服务包很重要,我也从未在MSDN上看到类似的页面。你必须去一个中国网站。例如,已检查的迭代器出现在VS2005 SP1(IIRC)中。
对于Linux和GCC兼容,GNUmakefile
检查编译器和汇编器的版本。如果它们太旧,则makefile会将CRYPTOPP_DISABLE_AESNI
添加到命令行以禁用支持,即使已定义__AES__
。
CRYPTOPP_DISABLE_AESNI
会更频繁地显示出来。例如,如果您下载OpenBSD 6.0(当前版本),那么
CRYPTOPP_DISABLE_AESNI
将会出现,因为他们的汇编程序太旧了。他们大多停留在他们工具的GPL-2版本之前(显然他们不同意许可证更改)。
第三,CPU支持AES和SSE4指令(下面解释了SSE4指令的原因)。这些检查在运行时执行,感兴趣的函数从cpu.h
调用HasAES()
(还有一个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 。据英特尔ARK称,它是处理器的可选配置。我们收到了一条关于AES-NI代码路径中非法SSE4指令的错误报告,因此我们必须添加HasSSE4()
项检查。请参阅PR 172, Check for SSE4 support before using SSE4.1 instruction。
如果是这样,如果硬件(较旧的CPU)不支持它会发生什么?
无。使用默认的CXX实现而不是硬件加速的AES。
您可能有兴趣知道我们还有其他AES硬件加速,包括ARMv8 Crypto和VIA Padlock。我们还提供其他硬件加速,如CRC32,Carryless-Multiplies和SHA。它们都以相同的方式运行 - 编译时支持转换为运行时支持。
(评论):我只是在cpu.cpp中的DetectX86Features方法上设置一个断点......它从未触发...
由于两个原因,这可能很棘手。首先,可以在发布版本中内联调用,因此代码的形状与您期望的形状略有不同。
其次,GlobalRNG()
访问了一个全局随机数生成器。 OFB模式下的GlobalRNG()
是AES。为test.cpp
翻译单元运行初始值设定项时,会创建GlobalRNG()
,这会导致DetectX86Features()
很早就开始运行(在控件进入main
之前)。
使用WinDbg观察低级细节可能会更好。
还值得一提的是AES / GCM可以通过将AES与GCM交错来加速。我相信这个想法是并行执行4轮AES密钥计算和1个CLMUL。 Crypto ++没有利用它,但OpenSSL抓住了机会。我不知道Botan或mbedTLS做了什么。
答案 2 :(得分:0)
刚刚完成我的问题,这是我的发现。
分支执行硬件支持的AES-NI
指令与我code sample above的Crypto ++库中软件实现的指令的方法Rijndael::Enc::AdvancedProcessBlocks
位于{ {1}}。它是这样开始的:
rijndael.cpp
如果您构建的Crypto ++库至少为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
预处理器变量(注意Visual Studio 2008 with SP1
很重要。)这种依赖关系是必要的。使用SP1
内在函数(例如_mm_aesenc_si128和_mm_aesenclast_si128)生成英特尔的AES-NI
机器代码指令。
因此在
的开头添加断点将允许您直接从Visual Studio进行调试。无需外部调试器。
如果您再进入AES-NI
方法,将使用其中一种AESNI_AdvancedProcessBlocks
方法处理实际的AES加密。以下是AESNI_Enc_*
版本中aesenclast
配置的实际aesenc
和x86
机器说明的外观:
因此,为了回答我原来的问题,对于上面帖子中的代码示例,为了能够利用英特尔的Release
指令,我们需要构建至少AES-NI
的代码示例和Crypto ++库。 (只需使用Visual Studio 2008 with SP1
或早期版本构建它,不会执行此工作,即使运行代码的CPU支持Visual Studio 2008
说明。)之后,似乎没有必要采取其他步骤。该库将自动检测AES-NI
指令的存在(AES-NI
函数),并在可用时使用它们。否则它将默认为软件实现。
最后,仅仅出于好奇,我决定看看硬件与软件AES-GCM加密在速度上会产生多大差异。我使用了以下代码片段(来自我上面的代码示例):
HasAESNI()
以下是两个结果:
和
这显然不是一个包罗万象的测试。我使用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);
CPU在桌面上运行它。
但好消息是即使没有硬件AES支持,"Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz"
加密似乎也非常快。