How to use a CNG (or AES-NI enabled instruction set) in .NET?
-
01-03-2021 - |
Pergunta
I Currently perform a large amount of encryption/decryption of text in c# using AES.
With a pure software system it can take quite a processor hit for a decent amount of time for the lots of datasets required to be decrypted. I know Intel have came out with their AES-NI instruction set and AMD has come out with similar.
I'm using .NET 4.0, I know that the windows CNG framework makes use of these instruction sets, but it does not appear that AesManaged
in the .NET world does the same.
There is a fantastic project "CLR Security" which makes a gateway from .NET 3.5 to the windows CNG, however it hasn't been maintained in a year and I'd rather not (if possible) jump on a dying project.
There is a CNGProvider class in .NET 4 but there doesn't appear to be adequate documentation to cobble together a working decryption from it for AES.
Does anyone have experience with the topic they could point me in the right direction on how to get AES-NI implemented in a pure .NET environment, using pre-made classes, without having to do a p/invoke directly from c#? (It'd be fine if there was a wrapper class doing it, as long as it was maintained).
Solução
What about
AesCryptoServiceProvider
? It says that uses CAPI, and so hopefully CNG if available. – Rup
This comment has helped tremendously, after doing some digging it looks like AesCryptoServiceProvider
will use AES-NI if available. I cannot find any 'official' documentation from Microsoft on this however. When running simple timing benchmarks the difference is ~15x faster so either the API itself is massively optimized (which for a 15x increase is pretty nice optimization) or it uses the AES-NI instruction set.
Unfortunately I don't have a non AES-NI box to test on, but if I ever get one I'll update this thread with results.
So I'm pretty confident this is the API to use for AES-NI but cannot guarantee without further testing.
Outras dicas
How to use a CNG (or AES-NI enabled instruction set) in .NET?
I'm going to focus on the AES-NI instruction set question. I found it to be interesting since I wondered it myself (for use in C and C++).
Microsoft added AES-NI support to Visual Studio 2008 SP1 (_MSC_FULL_VER >= 150030729
). The earliest you can observe AES-NI in Microsoft products is circa 2008 since earlier compilers did not support it. That means Server 2008 has it, and possibly Windows Vista via a Service Pack, and above.
According to Does MS Crypto API supports AES and AES-NI processor instructions?, both rsaenh.dll
and bcryptprimitives.dll
have it. The statements made by IvanP tested on Windows 7 and Windows 10.
However, testing on Windows 8.1 reveals...
# Using a Developer Command prompt so dumpbin is on-path:
> dumpbin /disasm c:\Windows\System32\rsaenh.dll > rsaenh.dll.txt
> dumpbin /disasm c:\Windows\System32\bcryptprimitives.dll > bcryptprimitives.dll.txt
Then:
# Using a GitBash terminal for grep
$ grep -i aes rsaenh.dll.txt
$
And:
$ grep -i aes bcryptprimitives.dll.txt
000000018000234A: 66 0F 3A DF C0 00 aeskeygenassist xmm0,xmm0,0
0000000180002363: 66 0F 38 DB C0 aesimc xmm0,xmm0
000000018000237E: 66 0F 38 DC 41 10 aesenc xmm0,xmmword ptr [rcx+10h]
0000000180002384: 66 0F 38 DC 41 20 aesenc xmm0,xmmword ptr [rcx+20h]
000000018000238A: 66 0F 38 DC 41 30 aesenc xmm0,xmmword ptr [rcx+30h]
0000000180002390: 66 0F 38 DC 41 40 aesenc xmm0,xmmword ptr [rcx+40h]
0000000180002396: 66 0F 38 DC 41 50 aesenc xmm0,xmmword ptr [rcx+50h]
000000018000239C: 66 0F 38 DC 41 60 aesenc xmm0,xmmword ptr [rcx+60h]
00000001800023A2: 66 0F 38 DC 41 70 aesenc xmm0,xmmword ptr [rcx+70h]
00000001800023AF: 66 0F 38 DC 01 aesenc xmm0,xmmword ptr [rcx]
00000001800023B4: 66 0F 38 DC 41 10 aesenc xmm0,xmmword ptr [rcx+10h]
00000001800023C3: 66 41 0F 38 DD 02 aesenclast xmm0,xmmword ptr [r10]
000000018001936E: 66 0F 38 DC 41 10 aesenc xmm0,xmmword ptr [rcx+10h]
0000000180019374: 66 0F 38 DC 41 20 aesenc xmm0,xmmword ptr [rcx+20h]
000000018001937A: 66 0F 38 DC 41 30 aesenc xmm0,xmmword ptr [rcx+30h]
0000000180019380: 66 0F 38 DC 41 40 aesenc xmm0,xmmword ptr [rcx+40h]
0000000180019386: 66 0F 38 DC 41 50 aesenc xmm0,xmmword ptr [rcx+50h]
000000018001938C: 66 0F 38 DC 41 60 aesenc xmm0,xmmword ptr [rcx+60h]
0000000180019392: 66 0F 38 DC 41 70 aesenc xmm0,xmmword ptr [rcx+70h]
000000018001939F: 66 0F 38 DC 01 aesenc xmm0,xmmword ptr [rcx]
00000001800193A4: 66 0F 38 DC 41 10 aesenc xmm0,xmmword ptr [rcx+10h]
00000001800193B3: 66 41 0F 38 DD 02 aesenclast xmm0,xmmword ptr [r10]
000000018001952E: 66 0F 38 DE C4 aesdec xmm0,xmm4
0000000180019533: 66 0F 38 DE CC aesdec xmm1,xmm4
0000000180019538: 66 0F 38 DE D4 aesdec xmm2,xmm4
000000018001953D: 66 0F 38 DE DC aesdec xmm3,xmm4
000000018001954B: 66 0F 38 DF C4 aesdeclast xmm0,xmm4
0000000180019550: 66 0F 38 DF CC aesdeclast xmm1,xmm4
0000000180019555: 66 0F 38 DF D4 aesdeclast xmm2,xmm4
000000018001955A: 66 0F 38 DF DC aesdeclast xmm3,xmm4
000000018002E8B5: 66 0F 38 DE 41 10 aesdec xmm0,xmmword ptr [rcx+10h]
000000018002E8BB: 66 0F 38 DE 41 20 aesdec xmm0,xmmword ptr [rcx+20h]
000000018002E8C1: 66 0F 38 DE 41 30 aesdec xmm0,xmmword ptr [rcx+30h]
000000018002E8C7: 66 0F 38 DE 41 40 aesdec xmm0,xmmword ptr [rcx+40h]
000000018002E8CD: 66 0F 38 DE 41 50 aesdec xmm0,xmmword ptr [rcx+50h]
000000018002E8D3: 66 0F 38 DE 41 60 aesdec xmm0,xmmword ptr [rcx+60h]
000000018002E8D9: 66 0F 38 DE 41 70 aesdec xmm0,xmmword ptr [rcx+70h]
000000018002E8E6: 66 0F 38 DE 01 aesdec xmm0,xmmword ptr [rcx]
000000018002E8EB: 66 0F 38 DE 41 10 aesdec xmm0,xmmword ptr [rcx+10h]
000000018002E8FA: 66 41 0F 38 DF 02 aesdeclast xmm0,xmmword ptr [r10]
000000018003F458: 66 0F 38 DC E8 aesenc xmm5,xmm0
000000018003F45D: 66 0F 38 DC D8 aesenc xmm3,xmm0
000000018003F462: 66 0F 38 DC E0 aesenc xmm4,xmm0
000000018003F467: 66 0F 38 DC F0 aesenc xmm6,xmm0
000000018003F475: 66 0F 38 DD EF aesenclast xmm5,xmm7
000000018003F47A: 66 0F 38 DD DF aesenclast xmm3,xmm7
000000018003F47F: 66 0F 38 DD E7 aesenclast xmm4,xmm7
000000018003F492: 66 0F 38 DD F7 aesenclast xmm6,xmm7
So, on modern Windows, you need to use something that depends on bcryptprimitives.dll
. I don't know what .Net primitives enlist bcryptprimitives.dll
.
I also checked the following DLLs on Windows 8.1, and there were no AES-NI instructions present:
- advapi32.dll
- bcrypt.dll
- crypt32.dll
- cryptbase.dll
- cryptcatsvc.dll
- cryptdlg.dll
- cryptdll.dll
- cryptext.dll
- cryptnet.dll
- cryptowinrt.dll
- cryptsp.dll
- cryptsvc.dll
There's not much information about the subject on Microsoft's site. I got two hits from csp "aes-ni" site:microsoft.com, and 0 hits for csp "aesni" site:microsoft.com. Whatever is going on, Microsoft is keeping it secret.
I had worked in the earliest of chipsets that had these features. At that time Windows 7 didn't have the ability to work with AES. I resorted to the then available Ubuntu and used the Intel doc and with some inline assembly did a cpuid. That served my purpose of a training. Nevertheless one thing I learned is "Intel secure key" was made available almost at the same time as "AES-NI". So if I found RDRAND instruction in the chipset that means I "probably" had AES-NI. So this way the simplest I can do (but not quite documented) is
//PF_RDRAND_INSTRUCTION_AVAILABLE is not documented yet as part of MSDN API as of //this writing IsProcessorFeaturePresent(PF_RDRAND_INSTRUCTION_AVAILABLE);
We can also use the CPU intrinsics this way https://docs.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=vs-2019
Also see below document for more "proof". https://csrc.nist.gov/csrc/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp1894.pdf