-
-
Notifications
You must be signed in to change notification settings - Fork 933
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use BCL ECDiffieHellman for KeyExchange instead of BouncyCastle (.NET 8.0 onward only) #1371
Conversation
Windows 8.1 and Windows Server 2012 R2 does not support ECDiffieHellman.DeriveRawSecretAgreement(ECDiffieHellmanPublicKey). Windows 8.1 LifecycleSupport Dates
Windows 8.1 EOL announcement: https://learn.microsoft.com/en-us/lifecycle/announcements/windows-8-1-end-support-january-2023 Windows Server 2012 R2 LifecycleSupport Dates
Releases
Windows Server 2012 R2 EOL announcement: https://learn.microsoft.com/en-us/lifecycle/announcements/windows-server-2012-r2-end-of-support Here's the link for Extended Security Updates Overview |
Thanks. It's a nice idea but I'm not sure it's worth the additional complexity at this time - with respect to the OS support and that we still have the old code which is no longer exercised in CI. Of course we could add a target for net6.0 on the integration tests but my gut feeling is that this change would be better left for the future. I could be convinced otherwise. |
Thanks, I updated the description with Advantages to elaborate on the change. I also updated note2. |
I think we don't need to worry about OS issue too much. Using an EOL Windows with the latest .NET 8.0+ seems bizarre to me. If they really want, they can use .NET Framework or .NET 6.0. What do you think? @Rob-Hague |
I don't think it's that bizarre, I have to deal with this exact situation at work myself. Our application is already on .NET 8 but there are quite a few customers who self-host and are still running Win2012. But I think dropping support for this would be fine as long as this is mentioned in the release notes. This would actually give me a good reason to drop support for Win2012 in our application. :) |
I think we can take this, if:
#if NET8_0_OR_GREATER
if (!OperatingSystem.IsWindows() || OperatingSystem.IsWindowsVersionAtLeast(10))
{
// BCL stuff
// wrap this in try { } catch (PlatformNotSupportedException) just to be safe?
return ...
}
#endif
// BouncyCastle fallback
We don't want to break anyone for no reason, and we will inevitably depend on BouncyCastle, so the only tangible benefit here might be performance |
Done except // wrap this in try { } catch (PlatformNotSupportedException) just to be safe? What do you mean by safe? |
I was thinking, since support does not seem to be universal across platforms, that maybe we wrap the BCL code in try-catch so that it still falls back to BouncyCastle if it's not supported. But I think it's OK, only Windows seems like a problem case from what I can tell: dotnet/runtime@be823a1 |
@@ -55,7 +55,7 @@ public override void Start(Session session, KeyExchangeInitMessage message, bool | |||
return; | |||
} | |||
#endif | |||
var curveParameter = SecNamedCurves.GetByName(CurveName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can tell, going through SshNamedCurves causes a bunch of extra lookup and static initialization compared to SecNamedCurves. Is there any reason to use it?
It's a shame we can't just directly refer to the static BC object when we know the curve we want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess SshNamedCurves
is designed for libraries like SSH.NET to consume?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, but I don't see why we would. If I run this program in a loop:
public static void Main()
{
var s = Stopwatch.StartNew();
//if (!SecNamedCurves.GetByName("secp256r1").G.IsInfinity)
if (!SshNamedCurves.GetByName("nistp256").G.IsInfinity)
{
Console.WriteLine(s.ElapsedTicks);
}
}
dotnet build -c Release; do { dotnet run -c Release --no-build } while ($true)
The result is about 28.3±0.7ms vs 29.2±0.8ms. Both extremely high (also includes first JIT of relevant BouncyCastle methods), but 1ms is 1ms
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you mind trying SecNamedCurves.GetByOid(SecObjectIdentifiers.SecP256r1)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't try it but I'm pretty sure it would be the same as SecNamedCurves.GetByName
. The problem with SshNamedCurves
is the amount of static constructors that it forces to run
Any idea here? |
Actually I'm more leaning towards to the policy "we don't implement cryptographic algorithms, but instead defer to an OS implementation" rather than performance improvement. |
Use one parameter constructor for class ECDomainParameters
Now the code structure is very similar with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks nice thanks
else | ||
{ | ||
_impl = new BouncyCastleImpl(CurveParameter); | ||
} | ||
#else | ||
_impl = new BouncyCastleImpl(CurveParameter); | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was going to suggest this but I imagine our style settings might have something to say about it.
else | |
{ | |
_impl = new BouncyCastleImpl(CurveParameter); | |
} | |
#else | |
_impl = new BouncyCastleImpl(CurveParameter); | |
#endif | |
else | |
#endif | |
{ | |
_impl = new BouncyCastleImpl(CurveParameter); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the style setting doesn't like it.
Here's the same situation: https://github.com/sshnet/SSH.NET/pull/1450/files#diff-4d37cacac3721823c238e98586d95622950554a40aff236a571339d813a53794R67-R72
…#1450) * [AesGcmCipher] Use BouncyCastle as a fallback if BCL does not support. * Switch back to collection initializer * Remove conditional compilation * Throw SshConnectionException with Reason MacError when authentication tag mismatch * Separate BCL and BouncyCastle implementation * Update AesGcmCipher.BclImpl.cs * Naming enhancement * Remove empty line * Disable S1199. See #1371 (comment) * Set InnerException when MAC error. Remove Message check. * Store KeyParameter as private field * Use GcmCipher.ProcessAadBytes to avoid the copy of associated data * Move nonce to constructor to avoid creating AeadParameters each packet * Use const int for tag size --------- Co-authored-by: Wojciech Nagórski <wojtpl2@gmail.com>
… (#1450) * [AesGcmCipher] Use BouncyCastle as a fallback if BCL does not support. * Switch back to collection initializer * Remove conditional compilation * Throw SshConnectionException with Reason MacError when authentication tag mismatch * Separate BCL and BouncyCastle implementation * Update AesGcmCipher.BclImpl.cs * Naming enhancement * Remove empty line * Disable S1199. See sshnet/SSH.NET#1371 (comment) * Set InnerException when MAC error. Remove Message check. * Store KeyParameter as private field * Use GcmCipher.ProcessAadBytes to avoid the copy of associated data * Move nonce to constructor to avoid creating AeadParameters each packet * Use const int for tag size --------- Co-authored-by: Wojciech Nagórski <wojtpl2@gmail.com>
So this won't remove the BouncyCastle reference for .NET 8+? That's a bummer for us since we are forbidden to ship BouncyCastle library, as it's been a historically high source of security vulnerabilities. |
Correct 😕 the library in previous versions was already using an internal point-in-time copy of BouncyCastle for ECDH, plus an internal point-in-time copy of Chaos.NaCl for ed25519. Now the supported BouncyCastle library is used for both. We decided the benefits of directly referencing the library (not having unmaintained copies of cryptography implementations) outweighed the drawbacks (size on disk, potential false positive vulnerabilities from unused parts of the library) Unfortunately, even .NET 9 does not provide the APIs we would need to remove third-party dependencies |
This PR leverages BCL's ECDiffieHellman for key exchange instead of using BouncyCastle.
Cryptographic operations in BCL are done by operating system (OS) libraries.
Advantages:
It inherits the advantages described in https://learn.microsoft.com/en-us/dotnet/standard/security/cross-platform-cryptography
Notes:
PlatformNotSupportedException
if the OS is Windows and below Windows 10. See this line of code. For this case, we use BouncyCastle as fallback.Based on below lifecycle table, I think it should be fine to throw.PlatformNotSupportedException
in Windows 8.1 and Windows Server 2012 R2 rather than use BouncyCastle as fallback. Then we can remove the BouncyCastle dependency when target .NET 8.0 onward