Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test for AES-NI instructions from C#

Tags:

c#

.net

aes

I want to know if there is a way to test for the presence of AES-NI in the host system's CPU from C#.NET.

Let me say up front that this question is not asking about how to use AES-NI from .NET. It turns out simply using AESCryptoServiceProvider will use AES-NI if it is available. This result is based on independent benchmarks I did comparing the performances of AESCryptoServiceProvider against the benchmarks provided in TrueCrypt, which does indeed support AES-NI. The results were surprisingly similar on both machines with and without AES-NI.

The reason I want to be able to test for it is to be able to indicate to the user that their computer supports AES-NI. This would be relevant since it would reduce support incidents involving questions like "but my friend has a Core i5 also but his is a lot faster!" If the program's user interface could indicate to the user that their system does or does not support AES-NI, it would also be possible to indicate that "slower performance is normal since this system does not support AES-NI."

(We can thank Intel for all of the confusion with different processor steppings! :-) )

Is there a way to detect this information, perhaps through WMI?

like image 592
fdmillion Avatar asked Jan 13 '15 04:01

fdmillion


People also ask

How do I know if my CPU has AES-NI?

Look in /proc/cpuinfo . If you have the aes flag then your CPU has AES support. , then you have AES.

How do you find AES-NI?

From the System Utilities screen, select System Configuration > BIOS/Platform Configuration (RBSU) > Server Security > Processor AES-NI Support.

Is AES the same as AES-NI?

AES-NI is up 13.5 times faster than AES on this Intel processor. AES-NI on ARMv8 processor can encrypt around 355 MB/S. AES-NI is up to 10 times faster than AES on the ARM processor. Results also show that the performance on linux in most cases for AES and AES-NI is better than windows OS for the same CPU.

How do you check if AES is enabled?

Check if AES-NI is Available on CPU Processors Before proceeding, first verify that current CPUs have the AES instruction set. For this you can inspect CPU flags as follows. If the output shows aes , that means AES-NI engine is available on current CPUs.


2 Answers

It's seems that there is the similar question on SO: Inline Assembly Code to Get CPU ID with the great answer.

But this answer requires some adjustments to suit your need.

First, as I understand, AES-NI can be presence at 64-bit processors only, right? Then you could ignore all 32-bit code in the answer above.

Second, you need ECX register or rather its 25th bit, so you must change code a bit:

private static bool IsAESNIPresent()
{
    byte[] sn = new byte[16]; // !!! Here were 8 bytes

    if (!ExecuteCode(ref sn))
        return false;

    var ecx = BitConverter.ToUInt32(sn, 8);
    return (ecx & (1 << 25)) != 0;
}

Finally, you need store ECX register in array:

byte[] code_x64 = new byte[] {
    0x53,                                     /* push rbx */
    0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, /* mov rax, 0x1 */
    0x0f, 0xa2,                               /* cpuid */
    0x41, 0x89, 0x00,                         /* mov [r8], eax */
    0x41, 0x89, 0x50, 0x04,                   /* mov [r8+0x4], ebx !!! changed */
    0x41, 0x89, 0x50, 0x08,                   /* mov [r8+0x8], ecx !!! added */
    0x41, 0x89, 0x50, 0x0C,                   /* mov [r8+0xC], edx !!! added*/
    0x5b,                                     /* pop rbx */
    0xc3,                                     /* ret */
};

As far as I can see, that's all changes.

like image 92
Mark Shevchenko Avatar answered Nov 19 '22 01:11

Mark Shevchenko


The answer from Mark above is fantastic and got things working well for me, however I did notice that if the application is run in 32 bit mode that the ecx register wasn't pulled in the x86 code thus leading to no detection of AES-NI.

I added one line and changed another, basically applying the changes Mark made to the x64 code to the x86 code. This allows you to see the AES-NI bit from 32 bit mode as well. Not sure if it will help someone, but I thought I'd post it.

EDIT: While I was doing some testing, I noticed that the registers returned by the x64 code were incorrect. EDX was being returned at offset 0x4, 0x8, and 0xC, additionally the ECX and EDX registers were at different offsets with x86 code so you needed to check IntPtr.Size more often to keep things working in both environments. To simplify things I place the ECX register at 0x4 and EDX at 0x8 and that way the data is arranged correctly.

If someone requests I can post up the entire class that is a working example of what I've learned from this post and others.

public static bool ExecuteCode(ref byte[] result) {
    byte[] code_x86 = new byte[] {
        0x55,                      /* push ebp */
        0x89, 0xE5,                /* mov  ebp, esp */
        0x57,                      /* push edi */
        0x8b, 0x7D, 0x10,          /* mov  edi, [ebp+0x10] */
        0x6A, 0x01,                /* push 0x1 */
        0x58,                      /* pop  eax */
        0x53,                      /* push ebx */
        0x0F, 0xA2,                /* cpuid    */
        0x89, 0x07,                /* mov  [edi], eax */
        0x89, 0x4F, 0x04,          /* mov  [edi+0x4], ecx Changed */
        0x89, 0x57, 0x08,          /* mov  [edi+0x8], edx Changed */
        0x5B,                      /* pop  ebx */
        0x5F,                      /* pop  edi */
        0x89, 0xEC,                /* mov  esp, ebp */
        0x5D,                      /* pop  ebp */
        0xC2, 0x10, 0x00,          /* ret  0x10 */
    };
    byte[] code_x64 = new byte[] {
        0x53,                                     /* push rbx */
        0x48, 0xC7, 0xC0, 0x01, 0x00, 0x00, 0x00, /* mov rax, 0x1 */
        0x0f, 0xA2,                               /* cpuid */
        0x41, 0x89, 0x00,                         /* mov [r8], eax */
        0x41, 0x89, 0x48, 0x04,                   /* mov [r8+0x4], ecx Changed */
        0x41, 0x89, 0x50, 0x08,                   /* mov [r8+0x8], edx Changed*/
        0x5B,                                     /* pop rbx */
        0xC3,                                     /* ret */
    };

    int num;
    byte[] code = (IntPtr.Size == 4) ? code_x86 : code_x64;
    IntPtr ptr = new IntPtr(code.Length);

    if (!VirtualProtect(code, ptr, PAGE_EXECUTE_READWRITE, out num))
        Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());

    ptr = new IntPtr(result.Length);

    return (ExecuteNativeCode(code, IntPtr.Zero, 0, result, ptr) != IntPtr.Zero);
like image 28
user9352383 Avatar answered Nov 19 '22 01:11

user9352383