Closed alxbsv closed 5 months ago
Tagging subscribers to this area: @dotnet/area-system-security, @bartonjs, @vcsjones See info in area-owners.md if you want to be subscribed.
Can you include the code that you used to create those measurements?
@vcsjones, added the code.
I can also try to include tables from Benchmark.NET, but don't have access to Ubuntu 20 at the moment. Also, not sure it's needed - problem should be easily reproducible.
Cryptographic objects like RSA
and ECDsa
do not generate a key in their constructor or Create
. They generate a key when they are needed, if a key has not been supplied.
Since you are not supplying a key, you are generating a key in ExportParameters
. And since you are using IterationSetup
, not GlobalSetup
, each run of the benchmark generates a key.
If we change the benchmark to force generating the key before it starts measuring generating the key, ExportParameters
does not differ meaningfully between OpenSSL 1.1 and 3.0 for me.
So, really your benchmark is showing that key generation is slower. As you noted, that is a known behavior of OpenSSL 3.0, and there isn’t much we can do about that.
using BenchmarkDotNet.Attributes;
using System.Security.Cryptography;
using BenchmarkDotNet.Running;
BenchmarkRunner.Run(typeof(Program).Assembly);
public class ExportParameters
{
protected const int RsaKeySize = 2048;
protected static readonly ECCurve EcdsaCurve = ECCurve.NamedCurves.nistP256;
private RSA _rsa = null!;
private ECDsa _ecdsa = null!;
[GlobalSetup]
public void GlobalSetup()
{
_rsa = RSA.Create(RsaKeySize);
_rsa.ExportParameters(false);
_ecdsa = ECDsa.Create(EcdsaCurve);
_ecdsa.ExportParameters(false);
}
[Benchmark]
public void Rsa() => _rsa.ExportParameters(false);
[Benchmark]
public void Ecdsa() => _ecdsa.ExportParameters(false);
}
@vcsjones, yes, the issue is about the key generation speed, not several Export*
calls on the same RSA instance. The use case is an endpoint for generating new RSA keys and saving them for later and it's now getting several times slower. Apologies if that wasn't clear.
I saw you've made some improvements regarding the Import*
performance downgrade after OpenSSL 3.0 - are any similar workarounds possible in this case? Like forcing the usage of the old algorithm for example?
Since RSAOpenSsl
implementation got an "update", yet Windows RSABCrypt
version remains the same - this means generation algorithms are quite different now. If no changes will be done to RSAOpenSsl
, shouldn't RSABCrypt
be updated to conform?
The only way we could fix this is to implement our own RSA key generator, which I don’t think is something we are willing to do since it is table stakes. We were able to fix the import because it only required implementing our own key check mechanism.
Ok, thanks for the quick turnaround.
First call to any of the
RSA.Export*
functions are around 4-5 times slower on Linux systems with OpenSSL 3.0 and later, than on Windows or distributions with OpenSSL 1.1. Only happens for key length of 2048 and larger. First call is where key generation happens, so, in other words,RSA
key generation got ~5 times slower since OpenSSL 3.Most likely cause is the corresponding update in OpenSSL, see https://github.com/openssl/openssl/issues/13370.
https://github.com/dotnet/runtime/issues/97727 is a similar issue regarding performance downgrade in
Import*
methods.Below are approximate measurements done on my machine, and, even considering that generation time is not consistent. these should be enough to demonstrate the issue:
ExportParameters(false)
, key length 2048RSABCrypt
RSAOpenSsl
RSAOpenSsl
RSAOpenSsl
RSAOpenSsl
ExportParameters(false)
, key length 1024RSABCrypt
RSAOpenSsl
RSAOpenSsl
RSAOpenSsl
Tested mostly on .NET 8.0.5, but for Windows and Ubuntu 20, numbers were similar between .NET 8, 7, 5 and Core 3.
Testing code used:
Alternative Benchmark.NET variant, showing similar results: