26

Tell me please, how can I check if OpenSSL is support/use the Intel AES-NI?

0

3 Answers 3

34

how can I check if OpenSSL is support/use the Intel AES-NI?

Its not that simple, though it should be. OpenSSL used to provide a function to get the capabilities detected for an ia32 processor, but its no longer available. See the discussion of OPENSSL_ia32cap_loc in the OPENSSL_ia32cap man page. Also see Verify AES-NI use at runtime? on the OpenSSL mailing list.

If you are linking to the OpenSSL static library, then you can use:

extern unsigned int OPENSSL_ia32cap_P[]; # define AESNI_CAPABLE (OPENSSL_ia32cap_P[1]&(1<<(57-32))) if(AESNI_CAPABLE) /* AES-NI is available */ 

If you are linking to the OpenSSL shared object, then the symbol OPENSSL_ia32cap_P is not exported. In this case, you need to write your own detection code.

I don't even bother with OpenSSL since it only works with static linking of the library. I shared the code I use for detection below. I believe I ripped a significant portion of it from Dave Johnston of Intel (he designed the RDRAND circuit).

Note: the code below could incorrectly reject an AMD processor with AES-NI. I don't have a processor to test on, so I can't offer the code.

Note: the code below will not perform as expected under Valgrind. There's no emulation for the AES-NI or RDRAND instructions, so Valgrind returns a "doctored" value from CPUID so it appears they are not available. See Incorrect results from inline assembly when running under Valgrind on the mailing list.


Even though AES-NI is available, it does not mean you are going to use it.

If you use the low level primitives like AES_*, then you will not use AES-NI because its a software implementation.

If you use the high level EVP_* gear, then you will use AES-NI if its available. The library will switch to AES-NI automatically.


If AES-NI is available but you don't want to use it, then perform the following before launching you program:

$ export OPENSSL_ia32cap="~0x200000200000000" 

You can test the speed difference with the following OpenSSL command. Toggle the export above to see the differences:

$ 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) ); } 
Sign up to request clarification or add additional context in comments.

3 Comments

How do you undo the export?
@user124384 Call unset OPENSSL_ia32cap.
The link for OPENSSL_ia32cap is broken. It looks like this is the current source: wiki.openssl.org/index.php/Manual:OPENSSL_ia32cap(3)
22

A couple quick one liners built from the information provided by jww:

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

Output of the first line should be significantly faster than the second. In my case on a i5 test machine, nearly double.

2 Comments

Shouldn't it be 4x or 8x as fast rather than double?
I got twice the performance for 16 size blocks, but nearly 14x for 8192 size blocks.
2

I made a Perl script (see below) that runs OpenSSL in default mode and with AES-NI explicitly disabled via an environment variable. It tests both CBC mode (earlier instructions like AESENC), and in GCM mode (later instructions such as PCLMULQDQ or VPCLMULQDQ). If the gain between the default mode of OpenSSL and the mode where AES-NI was explicitly disabled via an environment variable is more than 0% (on practice, should be at least 200%), than OpenSSL uses the new instructions. Here is a sample output of the script:

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% 

I've tested the script on Windows, Linux and MacOS. On Windows, Perl is not installed by default, but you can download and install any Perl implementation, for example, Strawberry Perl. In Linux and MacOS, Perl should be already installed. For MacOS, later version of Perl can be installed by "brew install perl" and following the instructions given by this command; however, for my script, that later version of Perl is not needed.

Still, regardless of the operating system, this script uses an environment variable for Intel-compatible processors (x86/IA-32 or x86-64/IA-64). As such, this script will not give proper results on ARM processors, such as those used on Mac Mini M1, because it will be unable to enable/disable special AES instructions.

Still, your question is on how can you check whether OpenSSL supports/uses the Intel AES-NI. Therefore, your question is limited for Intel-compatible processors that are supposed to have Intel AES-NI instructions.

Here is the script:

#!/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"; 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.