程序中如何启用openssl的AES-NI ?

昨天我贴了一个测试报告,openssl在启用了AES-NI后,做AES加解密的时候,效率有8-9倍的提升。但是当我自己写了一个程序去测试的时候,结果却并非如此。 首先,openssl有两套接口,一套EVP的(与具体算法无关),一套较低level的,针对特定算法的。 EVP的例子如下:

EVP_CIPHER_CTX ctx; 
EVP_CIPHER_CTX_init(&ctx);
ENGINE_load_builtin_engines();
ENGINE* engine=ENGINE_by_id("aesni");
if(engine==NULL){
    printf("aesni not found\n");            
}        
EVP_EncryptInit_ex(&ctx,EVP_aes_128_cbc(),engine,test_key_128,test_init_vector); 
int out; 
EVP_CipherUpdate(&ctx,output,&out, test_plain_text, inputlen);

需要特别说明的是:在openssl 1.0.0中,新添了一个叫做aesni的engine,只有当你指定了使用这个engine的时候,才会使用CPU的AES指令。但是到了openssl 1.0.1之后,这个engine被去除掉了,变成运行期自动监测是否使用AESNI。1.0.1版本中EVP_aes_128_cbc()这个函数的实现如下:
extern unsigned int OPENSSL_ia32cap_P[2];
#define AESNI_CAPABLE (OPENSSL_ia32cap_P[1]&(1\<\<(57-32))) const EVP_CIPHER *EVP_aes_128_cbc(void) { return AESNI_CAPABLE?&aesni_128_cbc:&aes_128_cbc; }
除了EVP,还有一套低级接口如下:

int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
    AES_KEY *key);
int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
    AES_KEY *key);
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
    size_t length, const AES_KEY *key,
    unsigned char *ivec, const int enc);

这些函数都是没有文档没有注释的。与这3个函数类似的,还有一套aesni_set_encrypt_key、aesni_set_decrypt_key、aesni_cbc_encrypt函数。但是这几个函数并不在安装后的头文件中。但是没有关系,可以自己声明一下然后用:

extern "C"{ 
    int aesni_set_encrypt_key(const unsigned char *userKey, const int bits, 
        AES_KEY *key); 
    int aesni_set_decrypt_key(const unsigned char *userKey, const int bits,
        AES_KEY *key);
    void aesni_cbc_encrypt(const unsigned char *in, unsigned char *out, 
        size_t length, const AES_KEY *key, 
        unsigned char *ivec, const int enc); 
}

然后我这还有一个函数,用来监测CPU是否支持AES-Ni:

#include <string.h> //memcmp

#ifndef __linux__
#include <intrin.h>
#else
static void __cpuid(unsigned int where[4], unsigned int leaf) {
  asm volatile("cpuid":"=a"(*where),"=b"(*(where+1)), 
               "=c"(*(where+2)),"=d"(*(where+3)):"a"(leaf));
  return;
}
#endif

#define AES_INSTRCTIONS_CPUID_BIT (1<<25)
bool check_for_aes_instructions()
{
#ifdef __linux__
        unsigned int cpuid_results[4];
#else   
        int cpuid_results[4];
#endif

        __cpuid(cpuid_results,0);

        if (cpuid_results[0] < 1)
                return false;
        /*
         *      MSB         LSB
         * EBX = 'u' 'n' 'e' 'G'
         * EDX = 'I' 'e' 'n' 'i'
         * ECX = 'l' 'e' 't' 'n'
         */        
        if (memcmp((unsigned char *)&cpuid_results[1], "Genu", 4) != 0 ||
              memcmp((unsigned char *)&cpuid_results[3], "ineI", 4) != 0 ||
              memcmp((unsigned char *)&cpuid_results[2], "ntel", 4) != 0)
              return false;

        __cpuid(cpuid_results,1);

        if (cpuid_results[2] & AES_INSTRCTIONS_CPUID_BIT)
                return true;

        return false;
}

够了!

此博客中的热门博文

少写代码,多读别人写的代码

在windows下使用llvm+clang

tensorflow distributed runtime初窥