Cryptography performance improvements on Windows in .NET 8 v .NET 7




Date Added (UTC):

07 Apr 2024 @ 22:21

Date Updated (UTC):

13 Apr 2024 @ 00:21


.NET Version(s):

.NET 7 .NET 8

Tag(s):

#.Net8PerfImprovement #Cryptography


Added By:
Profile Image

Blog   
Ireland    
.NET Developer and tech lead from Ireland!

Benchmark Results:





Benchmark Code:



using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Reports;
using System; 
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

[HideColumns("Error", "StdDev", "Median", "RatioSD")]
[MemoryDiagnoser(displayGenColumns: false)]
[SkipLocalsInit]
[Config(typeof(Config))]
public class Cryptography
{
    private static readonly RSA s_rsa = RSA.Create();
    private static readonly byte[] s_signed = s_rsa.SignHash(new byte[256 / 8], HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
    private static readonly byte[] s_encrypted = s_rsa.Encrypt(new byte[3], RSAEncryptionPadding.OaepSHA256);
    private static readonly X509Certificate2 s_cert = new X509Certificate2(Convert.FromBase64String("""
        MIIE7DCCA9SgAwIBAgITMwAAALARrwqL0Duf3QABAAAAsDANBgkqhkiG9w0BAQUFADB5MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
        UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSMwIQYDVQQDExpNaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQTAeFw0xMzAxMjQyMjMzMzlaFw0x
        NDA0MjQyMjMzMzlaMIGDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
        aW9uMQ0wCwYDVQQLEwRNT1BSMR4wHAYDVQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDor1yiIA34KHy8BXt/
        re7rdqwoUz8620B9s44z5lc/pVEVNFSlz7SLqT+oN+EtUO01Fk7vTXrbE3aIsCzwWVyp6+HXKXXkG4Unm/P4LZ5BNisLQPu+O7q5XHWTFlJLyjPFN7Dz636o9UEVXAhl
        HSE38Cy6IgsQsRCddyKFhHxPuRuQsPWj/ov0DJpOoPXJCiHiquMBNkf9L4JqgQP1qTXclFed+0vUDoLbOI8S/uPWenSIZOFixCUuKq6dGB8OHrbCryS0DlC83hyTXEmm
        ebW22875cHsoAYS4KinPv6kFBeHgD3FN/a1cI4Mp68fFSsjoJ4TTfsZDC5UABbFPZXHFAgMBAAGjggFgMIIBXDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU
        WXGmWjNN2pgHgP+EHr6H+XIyQfIwUQYDVR0RBEowSKRGMEQxDTALBgNVBAsTBE1PUFIxMzAxBgNVBAUTKjMxNTk1KzRmYWYwYjcxLWFkMzctNGFhMy1hNjcxLTc2YmMw
        NTIzNDRhZDAfBgNVHSMEGDAWgBTLEejK0rQWWAHJNy4zFha5TJoKHzBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9k
        dWN0cy9NaWNDb2RTaWdQQ0FfMDgtMzEtMjAxMC5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRz
        L01pY0NvZFNpZ1BDQV8wOC0zMS0yMDEwLmNydDANBgkqhkiG9w0BAQUFAAOCAQEAMdduKhJXM4HVncbr+TrURE0Inu5e32pbt3nPApy8dmiekKGcC8N/oozxTbqVOfsN
        4OGb9F0kDxuNiBU6fNutzrPJbLo5LEV9JBFUJjANDf9H6gMH5eRmXSx7nR2pEPocsHTyT2lrnqkkhNrtlqDfc6TvahqsS2Ke8XzAFH9IzU2yRPnwPJNtQtjofOYXoJto
        aAko+QKX7xEDumdSrcHps3Om0mPNSuI+5PNO/f+h4LsCEztdIN5VP6OukEAxOHUoXgSpRm3m9Xp5QL0fzehF1a7iXT71dcfmZmNgzNWahIeNJDD37zTQYx2xQmdKDku/
        Og7vtpU6pzjkJZIIpohmgg==
        """));

    [Benchmark]
    public void Encrypt()
    {
        Span<byte> src = stackalloc byte[3];
        Span<byte> dest = stackalloc byte[s_rsa.KeySize >> 3];
        s_rsa.TryEncrypt(src, dest, RSAEncryptionPadding.OaepSHA256, out _);
    }

    [Benchmark]
    public void Decrypt()
    {
        Span<byte> dest = stackalloc byte[s_rsa.KeySize >> 3];
        s_rsa.TryDecrypt(s_encrypted, dest, RSAEncryptionPadding.OaepSHA256, out _);
    }

    [Benchmark]
    public void Verify()
    {
        Span<byte> hash = stackalloc byte[256 >> 3];
        s_rsa.VerifyHash(hash, s_signed, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
    }

    [Benchmark]
    public void VerifyFromCert()
    {
        using RSA rsa = s_cert.GetRSAPublicKey();
        Span<byte> sig = stackalloc byte[rsa.KeySize >> 3];
        ReadOnlySpan<byte> hash = sig.Slice(0, 256 >> 3);
        rsa.VerifyHash(hash, sig, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
    }

    private class Config : ManualConfig
    {
        public Config()
        {
            AddJob(Job.Default.WithId(".NET 7").WithRuntime(CoreRuntime.Core70).AsBaseline());
            AddJob(Job.Default.WithId(".NET 8").WithRuntime(CoreRuntime.Core80));

            SummaryStyle =
                SummaryStyle.Default.WithRatioStyle(RatioStyle.Percentage);
        }
    }
}

// .NET 7, .NET 8
public void Encrypt()
{
    Span<byte> span = stackalloc byte[3];
    int bytesWritten = s_rsa.KeySize >> 3;
    Span<byte> destination = stackalloc byte[bytesWritten];
    s_rsa.TryEncrypt(span, destination, RSAEncryptionPadding.OaepSHA256, out bytesWritten);
}
// .NET 7, .NET 8
public void Decrypt()
{
    int bytesWritten = s_rsa.KeySize >> 3;
    Span<byte> destination = stackalloc byte[bytesWritten];
    s_rsa.TryDecrypt(s_encrypted, destination, RSAEncryptionPadding.OaepSHA256, out bytesWritten);
}
// .NET 7, .NET 8
public void Verify()
{
    Span<byte> span = stackalloc byte[32];
    s_rsa.VerifyHash(span, s_signed, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
// .NET 7, .NET 8
public void VerifyFromCert()
{
    RSA rSAPublicKey = RSACertificateExtensions.GetRSAPublicKey(s_cert);
    try
    {
        Span<byte> span = stackalloc byte[rSAPublicKey.KeySize >> 3];
        ReadOnlySpan<byte> hash = span.Slice(0, 32);
        rSAPublicKey.VerifyHash(hash, span, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
    }
    finally
    {
        if (rSAPublicKey != null)
        {
            ((IDisposable)rSAPublicKey).Dispose();
        }
    }
}

// .NET 7
.method public hidebysig 
    instance void Encrypt () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 31 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x20a0
    // Code size 61 (0x3d)
    .maxstack 5
    .locals (
        [0] valuetype [System.Runtime]System.Span`1<uint8> src,
        [1] valuetype [System.Runtime]System.Span`1<uint8> dest,
        [2] int32
    )

    // sequence point: (line 52, col 9) to (line 52, col 45) in _
    IL_0000: ldc.i4.3
    IL_0001: conv.u
    IL_0002: localloc
    IL_0004: ldc.i4.3
    IL_0005: newobj instance void valuetype [System.Runtime]System.Span`1<uint8>::.ctor(void*, int32)
    IL_000a: stloc.0
    // sequence point: (line 53, col 9) to (line 53, col 63) in _
    IL_000b: ldsfld class [System.Security.Cryptography]System.Security.Cryptography.RSA Cryptography::s_rsa
    IL_0010: callvirt instance int32 [System.Security.Cryptography]System.Security.Cryptography.AsymmetricAlgorithm::get_KeySize()
    IL_0015: ldc.i4.3
    IL_0016: shr
    IL_0017: stloc.2
    IL_0018: ldloc.2
    IL_0019: conv.u
    IL_001a: localloc
    IL_001c: ldloc.2
    IL_001d: newobj instance void valuetype [System.Runtime]System.Span`1<uint8>::.ctor(void*, int32)
    IL_0022: stloc.1
    // sequence point: (line 54, col 9) to (line 54, col 77) in _
    IL_0023: ldsfld class [System.Security.Cryptography]System.Security.Cryptography.RSA Cryptography::s_rsa
    IL_0028: ldloc.0
    IL_0029: call valuetype [System.Runtime]System.ReadOnlySpan`1<!0> valuetype [System.Runtime]System.Span`1<uint8>::op_Implicit(valuetype [System.Runtime]System.Span`1<!0>)
    IL_002e: ldloc.1
    IL_002f: call class [System.Security.Cryptography]System.Security.Cryptography.RSAEncryptionPadding [System.Security.Cryptography]System.Security.Cryptography.RSAEncryptionPadding::get_OaepSHA256()
    IL_0034: ldloca.s 2
    IL_0036: callvirt instance bool [System.Security.Cryptography]System.Security.Cryptography.RSA::TryEncrypt(valuetype [System.Runtime]System.ReadOnlySpan`1<uint8>, valuetype [System.Runtime]System.Span`1<uint8>, class [System.Security.Cryptography]System.Security.Cryptography.RSAEncryptionPadding, int32&)
    IL_003b: pop
    // sequence point: (line 55, col 5) to (line 55, col 6) in _
    IL_003c: ret
}

// .NET 8
.method public hidebysig 
    instance void Encrypt () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 31 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2050
    // Code size 61 (0x3d)
    .maxstack 5
    .locals (
        [0] valuetype [System.Runtime]System.Span`1<uint8> src,
        [1] valuetype [System.Runtime]System.Span`1<uint8> dest,
        [2] int32
    )

    // sequence point: (line 52, col 9) to (line 52, col 45) in _
    IL_0000: ldc.i4.3
    IL_0001: conv.u
    IL_0002: localloc
    IL_0004: ldc.i4.3
    IL_0005: newobj instance void valuetype [System.Runtime]System.Span`1<uint8>::.ctor(void*, int32)
    IL_000a: stloc.0
    // sequence point: (line 53, col 9) to (line 53, col 63) in _
    IL_000b: ldsfld class [System.Security.Cryptography]System.Security.Cryptography.RSA Cryptography::s_rsa
    IL_0010: callvirt instance int32 [System.Security.Cryptography]System.Security.Cryptography.AsymmetricAlgorithm::get_KeySize()
    IL_0015: ldc.i4.3
    IL_0016: shr
    IL_0017: stloc.2
    IL_0018: ldloc.2
    IL_0019: conv.u
    IL_001a: localloc
    IL_001c: ldloc.2
    IL_001d: newobj instance void valuetype [System.Runtime]System.Span`1<uint8>::.ctor(void*, int32)
    IL_0022: stloc.1
    // sequence point: (line 54, col 9) to (line 54, col 77) in _
    IL_0023: ldsfld class [System.Security.Cryptography]System.Security.Cryptography.RSA Cryptography::s_rsa
    IL_0028: ldloc.0
    IL_0029: call valuetype [System.Runtime]System.ReadOnlySpan`1<!0> valuetype [System.Runtime]System.Span`1<uint8>::op_Implicit(valuetype [System.Runtime]System.Span`1<!0>)
    IL_002e: ldloc.1
    IL_002f: call class [System.Security.Cryptography]System.Security.Cryptography.RSAEncryptionPadding [System.Security.Cryptography]System.Security.Cryptography.RSAEncryptionPadding::get_OaepSHA256()
    IL_0034: ldloca.s 2
    IL_0036: callvirt instance bool [System.Security.Cryptography]System.Security.Cryptography.RSA::TryEncrypt(valuetype [System.Runtime]System.ReadOnlySpan`1<uint8>, valuetype [System.Runtime]System.Span`1<uint8>, class [System.Security.Cryptography]System.Security.Cryptography.RSAEncryptionPadding, int32&)
    IL_003b: pop
    // sequence point: (line 55, col 5) to (line 55, col 6) in _
    IL_003c: ret
}
// .NET 7
.method public hidebysig 
    instance void Decrypt () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 39 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x20ec
    // Code size 54 (0x36)
    .maxstack 5
    .locals (
        [0] valuetype [System.Runtime]System.Span`1<uint8> dest,
        [1] int32
    )

    // sequence point: (line 60, col 9) to (line 60, col 63) in _
    IL_0000: ldsfld class [System.Security.Cryptography]System.Security.Cryptography.RSA Cryptography::s_rsa
    IL_0005: callvirt instance int32 [System.Security.Cryptography]System.Security.Cryptography.AsymmetricAlgorithm::get_KeySize()
    IL_000a: ldc.i4.3
    IL_000b: shr
    IL_000c: stloc.1
    IL_000d: ldloc.1
    IL_000e: conv.u
    IL_000f: localloc
    IL_0011: ldloc.1
    IL_0012: newobj instance void valuetype [System.Runtime]System.Span`1<uint8>::.ctor(void*, int32)
    IL_0017: stloc.0
    // sequence point: (line 61, col 9) to (line 61, col 85) in _
    IL_0018: ldsfld class [System.Security.Cryptography]System.Security.Cryptography.RSA Cryptography::s_rsa
    IL_001d: ldsfld uint8[] Cryptography::s_encrypted
    IL_0022: call valuetype [System.Runtime]System.ReadOnlySpan`1<!0> valuetype [System.Runtime]System.ReadOnlySpan`1<uint8>::op_Implicit(!0[])
    IL_0027: ldloc.0
    IL_0028: call class [System.Security.Cryptography]System.Security.Cryptography.RSAEncryptionPadding [System.Security.Cryptography]System.Security.Cryptography.RSAEncryptionPadding::get_OaepSHA256()
    IL_002d: ldloca.s 1
    IL_002f: callvirt instance bool [System.Security.Cryptography]System.Security.Cryptography.RSA::TryDecrypt(valuetype [System.Runtime]System.ReadOnlySpan`1<uint8>, valuetype [System.Runtime]System.Span`1<uint8>, class [System.Security.Cryptography]System.Security.Cryptography.RSAEncryptionPadding, int32&)
    IL_0034: pop
    // sequence point: (line 62, col 5) to (line 62, col 6) in _
    IL_0035: ret
}

// .NET 8
.method public hidebysig 
    instance void Decrypt () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 39 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x209c
    // Code size 54 (0x36)
    .maxstack 5
    .locals (
        [0] valuetype [System.Runtime]System.Span`1<uint8> dest,
        [1] int32
    )

    // sequence point: (line 60, col 9) to (line 60, col 63) in _
    IL_0000: ldsfld class [System.Security.Cryptography]System.Security.Cryptography.RSA Cryptography::s_rsa
    IL_0005: callvirt instance int32 [System.Security.Cryptography]System.Security.Cryptography.AsymmetricAlgorithm::get_KeySize()
    IL_000a: ldc.i4.3
    IL_000b: shr
    IL_000c: stloc.1
    IL_000d: ldloc.1
    IL_000e: conv.u
    IL_000f: localloc
    IL_0011: ldloc.1
    IL_0012: newobj instance void valuetype [System.Runtime]System.Span`1<uint8>::.ctor(void*, int32)
    IL_0017: stloc.0
    // sequence point: (line 61, col 9) to (line 61, col 85) in _
    IL_0018: ldsfld class [System.Security.Cryptography]System.Security.Cryptography.RSA Cryptography::s_rsa
    IL_001d: ldsfld uint8[] Cryptography::s_encrypted
    IL_0022: call valuetype [System.Runtime]System.ReadOnlySpan`1<!0> valuetype [System.Runtime]System.ReadOnlySpan`1<uint8>::op_Implicit(!0[])
    IL_0027: ldloc.0
    IL_0028: call class [System.Security.Cryptography]System.Security.Cryptography.RSAEncryptionPadding [System.Security.Cryptography]System.Security.Cryptography.RSAEncryptionPadding::get_OaepSHA256()
    IL_002d: ldloca.s 1
    IL_002f: callvirt instance bool [System.Security.Cryptography]System.Security.Cryptography.RSA::TryDecrypt(valuetype [System.Runtime]System.ReadOnlySpan`1<uint8>, valuetype [System.Runtime]System.Span`1<uint8>, class [System.Security.Cryptography]System.Security.Cryptography.RSAEncryptionPadding, int32&)
    IL_0034: pop
    // sequence point: (line 62, col 5) to (line 62, col 6) in _
    IL_0035: ret
}
// .NET 7
.method public hidebysig 
    instance void Verify () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 40 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2130
    // Code size 51 (0x33)
    .maxstack 5
    .locals (
        [0] valuetype [System.Runtime]System.Span`1<uint8> hash
    )

    // sequence point: (line 67, col 9) to (line 67, col 53) in _
    IL_0000: ldc.i4.s 32
    IL_0002: conv.u
    IL_0003: localloc
    IL_0005: ldc.i4.s 32
    IL_0007: newobj instance void valuetype [System.Runtime]System.Span`1<uint8>::.ctor(void*, int32)
    IL_000c: stloc.0
    // sequence point: (line 68, col 9) to (line 68, col 95) in _
    IL_000d: ldsfld class [System.Security.Cryptography]System.Security.Cryptography.RSA Cryptography::s_rsa
    IL_0012: ldloc.0
    IL_0013: call valuetype [System.Runtime]System.ReadOnlySpan`1<!0> valuetype [System.Runtime]System.Span`1<uint8>::op_Implicit(valuetype [System.Runtime]System.Span`1<!0>)
    IL_0018: ldsfld uint8[] Cryptography::s_signed
    IL_001d: call valuetype [System.Runtime]System.ReadOnlySpan`1<!0> valuetype [System.Runtime]System.ReadOnlySpan`1<uint8>::op_Implicit(!0[])
    IL_0022: call valuetype [System.Security.Cryptography]System.Security.Cryptography.HashAlgorithmName [System.Security.Cryptography]System.Security.Cryptography.HashAlgorithmName::get_SHA256()
    IL_0027: call class [System.Security.Cryptography]System.Security.Cryptography.RSASignaturePadding [System.Security.Cryptography]System.Security.Cryptography.RSASignaturePadding::get_Pkcs1()
    IL_002c: callvirt instance bool [System.Security.Cryptography]System.Security.Cryptography.RSA::VerifyHash(valuetype [System.Runtime]System.ReadOnlySpan`1<uint8>, valuetype [System.Runtime]System.ReadOnlySpan`1<uint8>, valuetype [System.Security.Cryptography]System.Security.Cryptography.HashAlgorithmName, class [System.Security.Cryptography]System.Security.Cryptography.RSASignaturePadding)
    IL_0031: pop
    // sequence point: (line 69, col 5) to (line 69, col 6) in _
    IL_0032: ret
}

// .NET 8
.method public hidebysig 
    instance void Verify () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 40 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x20e0
    // Code size 51 (0x33)
    .maxstack 5
    .locals (
        [0] valuetype [System.Runtime]System.Span`1<uint8> hash
    )

    // sequence point: (line 67, col 9) to (line 67, col 53) in _
    IL_0000: ldc.i4.s 32
    IL_0002: conv.u
    IL_0003: localloc
    IL_0005: ldc.i4.s 32
    IL_0007: newobj instance void valuetype [System.Runtime]System.Span`1<uint8>::.ctor(void*, int32)
    IL_000c: stloc.0
    // sequence point: (line 68, col 9) to (line 68, col 95) in _
    IL_000d: ldsfld class [System.Security.Cryptography]System.Security.Cryptography.RSA Cryptography::s_rsa
    IL_0012: ldloc.0
    IL_0013: call valuetype [System.Runtime]System.ReadOnlySpan`1<!0> valuetype [System.Runtime]System.Span`1<uint8>::op_Implicit(valuetype [System.Runtime]System.Span`1<!0>)
    IL_0018: ldsfld uint8[] Cryptography::s_signed
    IL_001d: call valuetype [System.Runtime]System.ReadOnlySpan`1<!0> valuetype [System.Runtime]System.ReadOnlySpan`1<uint8>::op_Implicit(!0[])
    IL_0022: call valuetype [System.Security.Cryptography]System.Security.Cryptography.HashAlgorithmName [System.Security.Cryptography]System.Security.Cryptography.HashAlgorithmName::get_SHA256()
    IL_0027: call class [System.Security.Cryptography]System.Security.Cryptography.RSASignaturePadding [System.Security.Cryptography]System.Security.Cryptography.RSASignaturePadding::get_Pkcs1()
    IL_002c: callvirt instance bool [System.Security.Cryptography]System.Security.Cryptography.RSA::VerifyHash(valuetype [System.Runtime]System.ReadOnlySpan`1<uint8>, valuetype [System.Runtime]System.ReadOnlySpan`1<uint8>, valuetype [System.Security.Cryptography]System.Security.Cryptography.HashAlgorithmName, class [System.Security.Cryptography]System.Security.Cryptography.RSASignaturePadding)
    IL_0031: pop
    // sequence point: (line 69, col 5) to (line 69, col 6) in _
    IL_0032: ret
}
// .NET 7
.method public hidebysig 
    instance void VerifyFromCert () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 47 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2170
    // Code size 84 (0x54)
    .maxstack 5
    .locals (
        [0] class [System.Security.Cryptography]System.Security.Cryptography.RSA rsa,
        [1] valuetype [System.Runtime]System.Span`1<uint8> sig,
        [2] valuetype [System.Runtime]System.ReadOnlySpan`1<uint8> hash,
        [3] int32
    )

    // sequence point: (line 74, col 9) to (line 74, col 50) in _
    IL_0000: ldsfld class [System.Security.Cryptography]System.Security.Cryptography.X509Certificates.X509Certificate2 Cryptography::s_cert
    IL_0005: call class [System.Security.Cryptography]System.Security.Cryptography.RSA [System.Security.Cryptography]System.Security.Cryptography.X509Certificates.RSACertificateExtensions::GetRSAPublicKey(class [System.Security.Cryptography]System.Security.Cryptography.X509Certificates.X509Certificate2)
    IL_000a: stloc.0
    .try
    {
        // sequence point: (line 75, col 9) to (line 75, col 60) in _
        IL_000b: ldloc.0
        IL_000c: callvirt instance int32 [System.Security.Cryptography]System.Security.Cryptography.AsymmetricAlgorithm::get_KeySize()
        IL_0011: ldc.i4.3
        IL_0012: shr
        IL_0013: stloc.3
        IL_0014: ldloc.3
        IL_0015: conv.u
        IL_0016: localloc
        IL_0018: ldloc.3
        IL_0019: newobj instance void valuetype [System.Runtime]System.Span`1<uint8>::.ctor(void*, int32)
        IL_001e: stloc.1
        // sequence point: (line 76, col 9) to (line 76, col 58) in _
        IL_001f: ldloca.s 1
        IL_0021: ldc.i4.0
        IL_0022: ldc.i4.s 32
        IL_0024: call instance valuetype [System.Runtime]System.Span`1<!0> valuetype [System.Runtime]System.Span`1<uint8>::Slice(int32, int32)
        IL_0029: call valuetype [System.Runtime]System.ReadOnlySpan`1<!0> valuetype [System.Runtime]System.Span`1<uint8>::op_Implicit(valuetype [System.Runtime]System.Span`1<!0>)
        IL_002e: stloc.2
        // sequence point: (line 77, col 9) to (line 77, col 88) in _
        IL_002f: ldloc.0
        IL_0030: ldloc.2
        IL_0031: ldloc.1
        IL_0032: call valuetype [System.Runtime]System.ReadOnlySpan`1<!0> valuetype [System.Runtime]System.Span`1<uint8>::op_Implicit(valuetype [System.Runtime]System.Span`1<!0>)
        IL_0037: call valuetype [System.Security.Cryptography]System.Security.Cryptography.HashAlgorithmName [System.Security.Cryptography]System.Security.Cryptography.HashAlgorithmName::get_SHA256()
        IL_003c: call class [System.Security.Cryptography]System.Security.Cryptography.RSASignaturePadding [System.Security.Cryptography]System.Security.Cryptography.RSASignaturePadding::get_Pkcs1()
        IL_0041: callvirt instance bool [System.Security.Cryptography]System.Security.Cryptography.RSA::VerifyHash(valuetype [System.Runtime]System.ReadOnlySpan`1<uint8>, valuetype [System.Runtime]System.ReadOnlySpan`1<uint8>, valuetype [System.Security.Cryptography]System.Security.Cryptography.HashAlgorithmName, class [System.Security.Cryptography]System.Security.Cryptography.RSASignaturePadding)
        IL_0046: pop
        // sequence point: (line 78, col 5) to (line 78, col 6) in _
        IL_0047: leave.s IL_0053
    }

// .NET 8
.method public hidebysig 
    instance void VerifyFromCert () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 47 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2120
    // Code size 84 (0x54)
    .maxstack 5
    .locals (
        [0] class [System.Security.Cryptography]System.Security.Cryptography.RSA rsa,
        [1] valuetype [System.Runtime]System.Span`1<uint8> sig,
        [2] valuetype [System.Runtime]System.ReadOnlySpan`1<uint8> hash,
        [3] int32
    )

    // sequence point: (line 74, col 9) to (line 74, col 50) in _
    IL_0000: ldsfld class [System.Security.Cryptography]System.Security.Cryptography.X509Certificates.X509Certificate2 Cryptography::s_cert
    IL_0005: call class [System.Security.Cryptography]System.Security.Cryptography.RSA [System.Security.Cryptography]System.Security.Cryptography.X509Certificates.RSACertificateExtensions::GetRSAPublicKey(class [System.Security.Cryptography]System.Security.Cryptography.X509Certificates.X509Certificate2)
    IL_000a: stloc.0
    .try
    {
        // sequence point: (line 75, col 9) to (line 75, col 60) in _
        IL_000b: ldloc.0
        IL_000c: callvirt instance int32 [System.Security.Cryptography]System.Security.Cryptography.AsymmetricAlgorithm::get_KeySize()
        IL_0011: ldc.i4.3
        IL_0012: shr
        IL_0013: stloc.3
        IL_0014: ldloc.3
        IL_0015: conv.u
        IL_0016: localloc
        IL_0018: ldloc.3
        IL_0019: newobj instance void valuetype [System.Runtime]System.Span`1<uint8>::.ctor(void*, int32)
        IL_001e: stloc.1
        // sequence point: (line 76, col 9) to (line 76, col 58) in _
        IL_001f: ldloca.s 1
        IL_0021: ldc.i4.0
        IL_0022: ldc.i4.s 32
        IL_0024: call instance valuetype [System.Runtime]System.Span`1<!0> valuetype [System.Runtime]System.Span`1<uint8>::Slice(int32, int32)
        IL_0029: call valuetype [System.Runtime]System.ReadOnlySpan`1<!0> valuetype [System.Runtime]System.Span`1<uint8>::op_Implicit(valuetype [System.Runtime]System.Span`1<!0>)
        IL_002e: stloc.2
        // sequence point: (line 77, col 9) to (line 77, col 88) in _
        IL_002f: ldloc.0
        IL_0030: ldloc.2
        IL_0031: ldloc.1
        IL_0032: call valuetype [System.Runtime]System.ReadOnlySpan`1<!0> valuetype [System.Runtime]System.Span`1<uint8>::op_Implicit(valuetype [System.Runtime]System.Span`1<!0>)
        IL_0037: call valuetype [System.Security.Cryptography]System.Security.Cryptography.HashAlgorithmName [System.Security.Cryptography]System.Security.Cryptography.HashAlgorithmName::get_SHA256()
        IL_003c: call class [System.Security.Cryptography]System.Security.Cryptography.RSASignaturePadding [System.Security.Cryptography]System.Security.Cryptography.RSASignaturePadding::get_Pkcs1()
        IL_0041: callvirt instance bool [System.Security.Cryptography]System.Security.Cryptography.RSA::VerifyHash(valuetype [System.Runtime]System.ReadOnlySpan`1<uint8>, valuetype [System.Runtime]System.ReadOnlySpan`1<uint8>, valuetype [System.Security.Cryptography]System.Security.Cryptography.HashAlgorithmName, class [System.Security.Cryptography]System.Security.Cryptography.RSASignaturePadding)
        IL_0046: pop
        // sequence point: (line 78, col 5) to (line 78, col 6) in _
        IL_0047: leave.s IL_0053
    }

// .NET 7 Jit Asm Code unavailable due to errors:
Type Cryptography has a static constructor, which is not supported by SharpLab JIT decompiler.
// .NET 8 Jit Asm Code unavailable due to errors:
Type Cryptography has a static constructor, which is not supported by SharpLab JIT decompiler.


Benchmark Description:


From the MS devblogs site : *One of the larger improvements, specific to Windows because it’s about switching what functionality is employed from the underlying OS, comes from dotnet/runtime#76277. Windows CNG (“Next Generation”) provides two libraries: bcrypt.dll and ncrypt.dll. The former provides support for “ephemeral” operations, ones where the cryptographic key is in-memory only and generated on the fly as part of an operation. The latter supports both ephemeral and persisted-key operations, and as a result much of the .NET support has been based on ncrypt.dll since it’s more universal. This, however, can add unnecessary expense, as all of the operations are handled out-of-process by the lsass.exe service, and thus require remote procedure calls, which add overhead. This PR switches RSA ephemeral operations over to using bcrypt instead of ncrypt, and the results are noteworthy (in the future, we expect other algorithms to also switch).* To run this one you need : ```<AllowUnsafeBlocks>true</AllowUnsafeBlocks>``` in your project file [PR](https://github.com/dotnet/runtime/pull/76277)

The provided benchmark code is designed to measure the performance of various cryptographic operations using the .NET framework, specifically focusing on RSA encryption, decryption, and signature verification. The benchmarks are set up to run on .NET 7 and .NET 8, allowing for a comparison between these two versions of the .NET runtime. This setup can help in understanding the performance improvements or regressions between different .NET versions. ### General Setup - **.NET Versions**: The benchmarks are configured to run on .NET 7 and .NET 8, providing a comparative analysis across these versions. - **Configuration**: The `Config` class specifies the runtime versions and sets the baseline for comparison. It also customizes the summary style to display ratios in percentage, making it easier to understand the performance differences. - **Memory Diagnoser**: Enabled with `displayGenColumns: false` to focus on the allocation size without displaying garbage collection generation columns. - **Columns**: The `HideColumns` attribute hides specific columns like Error, StdDev, Median, and RatioSD to streamline the output. - **SkipLocalsInit**: Optimizes the performance by skipping the initialization of locals, which can be beneficial for benchmark accuracy. ### Benchmark Methods #### 1. Encrypt - **Purpose**: Measures the performance of RSA encryption using OAEP SHA-256 padding. - **Importance**: Encryption speed is critical in scenarios where data needs to be encrypted at a high rate, such as in secure communications. - **Expected Insights**: This benchmark will reveal how quickly data can be encrypted using RSA, which is useful for assessing the overhead of adding encryption to an application. #### 2. Decrypt - **Purpose**: Measures the performance of RSA decryption using OAEP SHA-256 padding. - **Importance**: Decryption speed is crucial for applications that frequently need to decrypt data, such as when receiving encrypted messages. - **Expected Insights**: The results will show the efficiency of the decryption process, highlighting potential bottlenecks in secure data transmission or storage solutions. #### 3. Verify - **Purpose**: Tests the performance of verifying a signature with RSA using PKCS#1 SHA-256 padding. - **Importance**: Signature verification is a common operation in secure communications to ensure data integrity and authenticity. - **Expected Insights**: This benchmark provides insights into the overhead of signature verification, which is essential for applications that rely on digital signatures for security. #### 4. VerifyFromCert - **Purpose**: Measures the performance of extracting the public key from an X509 certificate and using it to verify a signature. - **Importance**: This operation is common in scenarios where certificates are used for secure communication, such as TLS/SSL. - **Expected Insights**: The results will indicate the efficiency of handling certificates and verifying signatures in .NET, which affects the performance of applications using certificate-based authentication or encryption. ### Conclusion Running these benchmarks will provide valuable insights into the performance characteristics of cryptographic operations in .NET, particularly across different versions. This information can guide developers in optimizing their applications for better security performance and understanding the impact of upgrading to newer .NET versions.


Benchmark Comments: