加密是互联网数据安全的保证,但是现在感觉多数网页游戏和手游都已经抛弃了数据加密,几乎都是直接明文发送。不过现在游戏烂大街,除非很牛的游戏才会有人去破解,垃圾游戏我想没有那个蛋疼的会去破解。本着学习的精神下载了crypto++ 对常用的对称加密算法进行性能比较,由于游戏数据包都比较小,所以测试中选择的最大数据长度为200k,我想如果一个游戏数据包超过200k那只能是加载资源的时候了,其他时候没有人受的了长时间的转菊花。曾经在项目中有一次因为特殊原因弄了一个100k的数据包直接客户端超时。下面的测试中应用了vs2102的debug和release,gcc4.8+O2优化,clang3.5 +O2优化。加密算法后面带_EX表示从crypto++中或者在网上搜索的加密算法进行自己封装的函数的性能测试,这样可以减少函数调用,也利于以后在项目中使用,不用承受庞大的代码库,模板等其他开销。所有测试都是进行100次加密后计算的平均值,时间单位为微妙,如果为0表示平均之后时间不到1微妙,AES加密是应用了AES-NI指令,AES_EX测试的时间是应用了AES-NI指令的四路并行加密,具体算法可以看crypto++实现。所有加密都使用ECB模式(没有进行异或处理)。测试机子就是一台普通笔记本,gcc和clang测试在ubuntu13.4虚拟机上进行测试的。
测试结果如下:
总结:
从测试结果发现AES算法在支持AES-NI指令并且使用四路并行加密是游戏加密的首选,因为在各种情况下都是最好的,200k的数据不到1毫妙,这在高并发游戏中是可以接收的加密算法。如果不支持AES-NI指令则AES算法性能会降低10倍左右,只进行了一次测试,具体数值需要做更多测试。如果不支持AES-NI指令则选择XXTEA比较好,200k能够在4毫秒以内还是可以接受的,并且一般来说游戏中90%的协议在1k以内。以上测试只是进行简单的比较,应用不同对加密性能和安全也不同。所以还是需要根据项目选择合适的加密算法。
番外:
在将crypto++中AES算法提取出来自己封装的时候AES-NI指令在linux中编译报错,报错信息很多,下面是开始的报错信息:
usr/lib/gcc/x86_64-linux-gnu/4.8/include/wmmintrin.h: In function ‘__m128i _mm_aesdec_si128(__m128i, __m128i)’:
/usr/lib/gcc/x86_64-linux-gnu/4.8/include/wmmintrin.h:45:70: error: ‘__builtin_ia32_aesdec128’ was not declared in this scope
这个报错是GCC中的wmmintrin.h 头文件需要一些预定宏,也就是他会检查cpu是否支持AES-NI指令 度娘根本找不到,谷歌这几天ip都不能用了,最后只好看了下crypto++的makefile文件找到了答案,在编译选择中加上-march=native 选择,编译器会自动检查本机的cpu指令集,这样就ok了。如果想编译出来在其他主机上使用需要保证机子的cpu支持相同的指令集主要是AES-NI和SSE2,等指令。在自己提取AES算法的时候圈密钥需要限制16字节对齐要不是在使用AES-NI指令的时候会crash,具体如下:
#if defined(CRYPTOPP_MSVC6PP_OR_LATER)
#define CRYPTOPP_ALIGN_DATA(x) __declspec(align(x))
#elif defined(__GNUC__)
#define CRYPTOPP_ALIGN_DATA(x) __attribute__((aligned(x)))
#else
#define CRYPTOPP_ALIGN_DATA(x)
#endif
CRYPTOPP_ALIGN_DATA(16) uint32_t pTabKey[60]={0};
pTabKey是用于加密的圈密钥,这样他的起始地址摸16会为0,这样在使用_mm_aesimc_si128,_mm_aesenc_si128等这些指令的时候才不会又问题。如果要动态分配内存好像需要_mm_malloc函数,没用进行测试。
在测试中发现AES加密中的Rijndael_Enc_AdvancedProcessBlocks这个函数加密后解密不出来,这个函数是应用SSE2指令集编写的一个加密函数,里面全是汇编语言所以也就没有去深入研究了,另外在使用AES-NI的加密使用小端进行加解密,而crypto++中AES普通加解密算法使用的是大端进行加解密,所以crypto++库中的AES-NI加密的数据不能用库中的普通解密算法解密,反之也不行。