如何检查 OpenSSL 是否支持/使用 Intel AES-NI?

问题描述 投票:0回答:3

请告诉我,如何检查 OpenSSL 是否支持/使用 Intel AES-NI?

openssl aes
3个回答
32
投票

如何检查 OpenSSL 是否支持/使用 Intel AES-NI?

事情没那么简单,虽然应该如此。 OpenSSL 曾经提供了一个函数来获取 ia32 处理器检测到的功能,但它不再可用。请参阅

OPENSSL_ia32cap_loc 手册页
 中对 
OPENSSL_ia32cap
的讨论。另请参阅 OpenSSL 邮件列表上的在运行时验证 AES-NI 使用?

如果您要链接到 OpenSSL 静态库,那么您可以使用:

extern unsigned int OPENSSL_ia32cap_P[];
# define AESNI_CAPABLE (OPENSSL_ia32cap_P[1]&(1<<(57-32)))

if(AESNI_CAPABLE)
    /* AES-NI is available */

如果您要链接到 OpenSSL 共享对象,则符号

OPENSSL_ia32cap_P
不会导出。在这种情况下,您需要编写自己的检测代码。

我什至不关心 OpenSSL,因为它只适用于库的静态链接。我在下面分享了我用于检测的代码。我相信我从英特尔的戴夫·约翰斯顿(Dave Johnston)那里窃取了其中的很大一部分(他设计了 RDRAND 电路)。

注意:下面的代码可能会错误地拒绝具有 AES-NIAMD 处理器。我没有可供测试的处理器,因此无法提供代码。

注意:下面的代码在 Valgrind 下不会按预期执行。没有针对 AES-NI 或 RDRAND 指令的模拟,因此 Valgrind 从

CPUID
返回一个“经过修改的”值,因此看起来它们不可用。请参阅邮件列表上的在 Valgrind 下运行时内联汇编的错误结果


尽管 AES-NI 可用,但它意味着您将使用它。

如果您使用像

AES_*
这样的低级原语,那么您将不会使用AES-NI,因为它是软件实现。

如果您使用高级

EVP_*
齿轮,那么您将使用AES-NI(如果可用)。库将自动切换到 AES-NI。


如果 AES-NI 可用但您不想使用它,请在启动程序之前执行以下操作:

$ export OPENSSL_ia32cap="~0x200000200000000"

您可以使用以下 OpenSSL 命令测试速度差异。切换上面的导出以查看差异:

$ openssl speed -elapsed -evp aes-128-ecb

struct CPUIDinfo {
    unsigned int EAX;
    unsigned int EBX;
    unsigned int ECX;
    unsigned int EDX;
};

int HasIntelCpu();
int HasAESNI();
int HasRDRAND();

void cpuid_info(CPUIDinfo *info, const unsigned int func,
        const unsigned int subfunc);

int HasIntelCpu() {
    CPUIDinfo info;
    cpuid_info(&info, 0, 0);
    if (memcmp((char *) (&info.EBX), "Genu", 4) == 0
            && memcmp((char *) (&info.EDX), "ineI", 4) == 0
            && memcmp((char *) (&info.ECX), "ntel", 4) == 0) {

        return 1;
    }

    return 0;
}

int HasAESNI() {
    if (!HasIntelCpu())
        return 0;

    CPUIDinfo info;
    cpuid_info(&info, 1, 0);

    static const unsigned int AESNI_FLAG = (1 << 25);
    if ((info.ECX & AESNI_FLAG) == AESNI_FLAG)
        return 1;

    return 0;
}

int HasRDRAND() {

    if (!HasIntelCpu())
        return 0;

    CPUIDinfo info;
    cpuid_info(&info, 1, 0);

    static const unsigned int RDRAND_FLAG = (1 << 30);
    if ((info.ECX & RDRAND_FLAG) == RDRAND_FLAG)
        return 1;

    return 0;
}

void cpuid_info(CPUIDinfo *info, unsigned int func, unsigned int subfunc) {
    __asm__ __volatile__ (
            "cpuid"
            : "=a"(info->EAX), "=b"(info->EBX), "=c"(info->ECX), "=d"(info->EDX)
            : "a"(func), "c"(subfunc)
    );
}

20
投票

根据jww提供的信息构建了几个快速衬里:

openssl 速度 -elapsed -evp aes-128-cbc
...
OPENSSL_ia32cap="~0x200000200000000" openssl 速度 -elapsed -evp aes-128-cbc
...

第一行的输出应该明显快于第二行。就我而言,在 i5 测试机上,几乎翻了一番。


0
投票

我制作了一个 Perl 脚本(见下文),它在默认模式下运行 OpenSSL,并通过环境变量显式禁用 AES-NI。它测试 CBC 模式(早期指令,如

AESENC
)和 GCM 模式(后来的指令,如
PCLMULQDQ
VPCLMULQDQ
)。如果 OpenSSL 的默认模式与通过环境变量显式禁用 AES-NI 的模式之间的增益大于 0%(实际上,应至少为 200%),则 OpenSSL 使用新指令。这是脚本的示例输出:

AES-CBC In default mode      :     1641669.67k bytes/second, on average
AES-CBC With AES-NI disabled :      448189.38k bytes/second, on average
AES-CBC Gain                 :         266.29%
AES-GCM In default mode      :     4112319.92k bytes/second, on average
AES-GCM With AES-NI disabled :      272767.79k bytes/second, on average
AES-GCM Gain                 :        1407.63%

我已经在 Windows、Linux 和 MacOS 上测试了该脚本。在 Windows 上,默认情况下不安装 Perl,但您可以下载并安装任何 Perl 实现,例如 Strawberry Perl。在 Linux 和 MacOS 中,Perl 应该已经安装。对于 MacOS,可以通过“brew install perl”并按照此命令给出的说明安装更高版本的 Perl;但是,对于我的脚本,不需要更高版本的 Perl。

尽管如此,无论操作系统如何,此脚本都会使用 Intel 兼容处理器(x86/IA-32 或 x86-64/IA-64)的环境变量。因此,此脚本不会在 ARM 处理器(例如 Mac Mini M1 上使用的处理器)上给出正确的结果,因为它将无法启用/禁用特殊的 AES 指令。

您的问题仍然是如何检查 OpenSSL 是否支持/使用 Intel AES-NI。因此,您的问题仅限于应该具有 Intel AES-NI 指令的 Intel 兼容处理器。

这是脚本:

#!/usr/bin/perl
use strict;

my $formatstr = 'The OpenSSL output does not match the regular expression.\nRegEx: %s\n---output begin---\n%s\n---output end---:';
my $regex_cbc = qr/^\s*AES-128-CBC\s+(\d+\.\d+)k\s+(\d+\.\d+)k\s+(\d+\.\d+)k\s+(\d+\.\d+)k\s+(\d+\.\d+)k\s+(\d+\.\d+)k\s*$/m;
my $regex_gcm = qr/^\s*AES-128-GCM\s+(\d+\.\d+)k\s+(\d+\.\d+)k\s+(\d+\.\d+)k\s+(\d+\.\d+)k\s+(\d+\.\d+)k\s+(\d+\.\d+)k\s*$/m;
my $command_cbc = 'openssl speed -elapsed -seconds 1 -evp aes-128-cbc';
my $command_gcm = 'openssl speed -elapsed -seconds 1 -evp aes-128-gcm';
my $env_var_name = 'OPENSSL_ia32cap';
my $env_var_value_disable_aes_ni = '~0x200000200000000';


# Test the CBC mode

delete $ENV{$env_var_name};
print "Running OpenSSL for AES-128-CBC in default mode...\n";
sleep(1); # Cooldown for the CPU before the benchmark
my $defaultrun_cbc = `$command_cbc`;
chomp $defaultrun_cbc;

print "Running OpenSSL for AES-128-CBC with AES-NI explicityly disabled...\n";
sleep(1); # Cooldown for the CPU before the benchmark
$ENV{$env_var_name}=$env_var_value_disable_aes_ni;
my $disabledrun_cbc = `$command_cbc`;
chomp $disabledrun_cbc;


# Test the GCM mode

delete $ENV{$env_var_name};
print "Running OpenSSL for AES-128-GCM in default mode...\n";
sleep(1); # Cooldown for the CPU before the benchmark
my $defaultrun_gcm = `$command_gcm`;
chomp $defaultrun_gcm;

print "Running OpenSSL for AES-128-GCM with AES-NI explicityly disabled...\n";
sleep(1); # Cooldown for the CPU before the benchmark
$ENV{$env_var_name}=$env_var_value_disable_aes_ni;
my $disabledrun_gcm = `$command_gcm`;
chomp $disabledrun_gcm;


# Calculate the results

my $total_default_cbc; my $total_disabled_cbc; my $total_default_gcm; my $total_disabled_gcm;

if ( $defaultrun_cbc =~ $regex_cbc )  {$total_default_cbc =  ( $1 + $2 + $3 + $4 + $5 + $6 ) / 6;} else {die sprintf( $formatstr, $regex_cbc, $defaultrun_cbc );}
if ( $disabledrun_cbc =~ $regex_cbc ) {$total_disabled_cbc = ( $1 + $2 + $3 + $4 + $5 + $6 ) / 6;} else {die sprintf( $formatstr, $regex_cbc, $disabledrun_cbc);}
my $percentage_gain_cbc = 100 * ( $total_default_cbc - $total_disabled_cbc ) / $total_disabled_cbc;

if ( $defaultrun_gcm =~ $regex_gcm )  {$total_default_gcm =  ( $1 + $2 + $3 + $4 + $5 + $6 ) / 6;} else {die sprintf( $formatstr, $regex_gcm, $defaultrun_gcm );}
if ( $disabledrun_gcm =~ $regex_gcm ) {$total_disabled_gcm = ( $1 + $2 + $3 + $4 + $5 + $6 ) / 6;} else {die sprintf( $formatstr, $regex_gcm, $disabledrun_gcm);}
my $percentage_gain_gcm = 100 * ( $total_default_gcm - $total_disabled_gcm ) / $total_disabled_gcm;


# Print the results

print "\n\n";
print "AES-CBC In default mode      : " . sprintf( "\%14.2f", $total_default_cbc  ) . "k bytes/second, on average\n";
print "AES-CBC With AES-NI disabled : " . sprintf( "\%14.2f", $total_disabled_cbc ) . "k bytes/second, on average\n";
print "AES-CBC Gain                 : " . sprintf( "\%14.2f", $percentage_gain_cbc) . "%\n";
print "AES-GCM In default mode      : " . sprintf( "\%14.2f", $total_default_gcm  ) . "k bytes/second, on average\n";
print "AES-GCM With AES-NI disabled : " . sprintf( "\%14.2f", $total_disabled_gcm ) . "k bytes/second, on average\n";
print "AES-GCM Gain                 : " . sprintf( "\%14.2f", $percentage_gain_gcm) . "%\n";

© www.soinside.com 2019 - 2024. All rights reserved.