-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
SSH from .NET Core support #69130
Comments
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
Tagging subscribers to this area: @dotnet/ncl, @vcsjones Issue DetailsDuring the work with a customer, discovered that .NET core doesn't contain SSH which was crucial for the development of the engagement. Using a 3rd party NuGet package (known as SSH.NET) was the workaround, which unfortunately is not well maintained and supported, which brings the following issues:
it is very common to find SSH as a standard mechanism to communicate between services across different platform, which brings the issue here whether this should be considered as workable item.
|
related to #40424 and #31437. We may choose to contribute to https://github.com/sshnet/SSH.NET and perhaps bring it under https://dotnetfoundation.org/ umbrella. |
Triage: we want to see how much demand is from community, and consider it in the future. Please upvote top post if interested. |
Previously we had interest in: |
I think @tmds made or started an SSH client library and noted that there are some possible missing cryptographic pieces, particularly around DiffieHellman, if I recall correctly. SSH needs the "raw" DiffieHellman secret where we don't provide it. SSH.NET gets around this by implementing a lot of the cryptography by hand or borrowing from BouncyCastle. Is that right @tmds? |
That's right. There is no API that returns the shared secret. And if I remember correctly, Windows itself doesn't provide one. After trying the managed implementation on top of .NET crypto APIs, I did another prototype that wraps libssh. The code is at https://github.com/tmds/Tmds.Ssh. (The managed implementation is under the |
/cc @SteveL-MSFT Could you please share thoughts on the possibility of implementing this request? |
I think this is possible with But I digress. |
To follow up on raw DH, .NET 8 has |
I started the Tmds.Ssh repo with a managed implementation. After I got stuck on the DH secret, I switched it to wrap libssh instead. I'm going to circle back to the managed implementation and see if I can now get it to fully work using only .NET BCL crypto. I'm posting here to raise some awareness. It may take some weeks until I actually get this done. |
I have this working. Here are some "getting started" steps: https://github.com/tmds/Tmds.Ssh?tab=readme-ov-file#getting-started. If you have feedback, you can create an issue or starting a discussion in the repo. |
I'm looking at the algorithms my OpenSSH client would prefer to use (see below), and these are some preferable algorithms that .NET doesn't provide APIs for:
This is the list of algorithms currently provided through BCL crypto in Tmds.Ssh: https://github.com/tmds/Tmds.Ssh?tab=readme-ov-file#tmdsssh.
|
You can implement ChaCha20 with ChaCha20Poly1305 (basically remove the Poly1305). Basically, you just use For Here is an implementation of ChaCha20: public sealed class ChaCha20 : IDisposable {
private readonly ChaCha20Poly1305 _chaCha20Poly1305;
public ChaCha20(byte[] key) => _chaCha20Poly1305 = new(key);
public ChaCha20(ReadOnlySpan<byte> key) => _chaCha20Poly1305 = new(key);
public void Encrypt(byte[] nonce, byte[] plaintext, byte[] ciphertext) {
ArgumentNullException.ThrowIfNull(nonce);
ArgumentNullException.ThrowIfNull(plaintext);
ArgumentNullException.ThrowIfNull(ciphertext);
Span<byte> tagThrowAway = stackalloc byte[16];
_chaCha20Poly1305.Encrypt(nonce, plaintext, ciphertext, tagThrowAway);
}
public void Encrypt(ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> plaintext, Span<byte> ciphertext) {
Span<byte> tagThrowAway = stackalloc byte[16];
_chaCha20Poly1305.Encrypt(nonce, plaintext, ciphertext, tagThrowAway);
}
// ChaCha20 is a stream cipher that builds a keystream and XORs the keystream with the data.
// So encrypt and decrypt are actually the same thing. We use encrypt here because it allows us to throw away
// the tag, essentially decryption without providing a tag.
public void Decrypt(byte[] nonce, byte[] ciphertext, byte[] plaintext) =>
Encrypt(nonce, ciphertext, plaintext);
public void Decrypt(ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> ciphertext, Span<byte> plaintext) =>
Encrypt(nonce, ciphertext, plaintext);
public void Dispose() => _chaCha20Poly1305.Dispose();
} And to test it: ReadOnlySpan<byte> key = [
00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31];
ChaCha20 chaCha20 = new(key);
ReadOnlySpan<byte> plaintext = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."u8;
ReadOnlySpan<byte> nonce = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00];
Span<byte> ciphertextBuffer = stackalloc byte[plaintext.Length];
Span<byte> plaintextBuffer = stackalloc byte[plaintext.Length];
chaCha20.Encrypt(nonce, plaintext, ciphertextBuffer);
Console.WriteLine(Convert.ToHexString(ciphertextBuffer));
chaCha20.Decrypt(nonce, ciphertextBuffer, plaintextBuffer);
Console.WriteLine(Encoding.UTF8.GetString(plaintextBuffer)); We can see this roundtrips the encryption and decryption appropriately, and produces the same cipher text (Ciphertext Sunscreen) as the RFC. This does mean the implementation is doing Poly1305 for no reason, but Poly1305 overhead is fairly small.
I don't know if .NET will ever have a CTR mode in the box. But, this one is reasonable to implement on top of AES-ECB.
There is no good work around for these currently, unfortunately, and nor are they trivial to implement. As you have found we have (several) tracking issues for various 25519 curve uses, but impeded by some operating systems. |
@vcsjones thanks for providing code for this!
If you could provide an implementation, it would be very welcome too. In the next months I want to release a 0.2 version that includes both a managed and libssh based implementation. |
@vcsjones I'm doing some reading on ChaCha20Poly1305. As I understand it, the algorithm has a block counter and counter zero is used to derive a key. The counter then increments for encrypting the plaintext. To implement chacha20-poly1305@openssh.com (spec) we need to encrypt with the zero block counter. I think this means we can't use |
I got the exact problem when try to bring |
I think we need a class to provide ChaCha20 separately which could be something like: namespace System.Security.Cryptography;
class ChaCha20 : IDisposable
{
static bool IsSupported;
ChaCha20(ReadOnlySpan<byte> key, int initialCounter);
void Transform(ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> input, Span<byte> output);
} This would be usable for the packet length field encryption of chacha20-poly1305@openssh.com. For the entire packet encryption, I think the algorithm described in chacha20-poly1305@openssh.com matches with what ChaCha20Poly1305 provides. |
Unfortunately a number of the platform crypto primitives do not expose unauthenticated ChaCha20 which accepts an initial counter.
I also do not know if there is much appetite for adding unauthenticated |
I'm trying to bring chacha20-poly1305@openssh.com into SSH.NET.
However, the openssh server doesn't like the Here's the PR: sshnet/SSH.NET#1416 |
@vcsjones do you know if they start counting at zero?
@scott-xu It looks like you got it working (CI is passing). You aren't using the BCL |
@tmds I'm now using both home-made managed
while the OpenSSH's
|
@vcsjones Do you mean I'm trying to check the Windows CNG myself. However, there's even no "chacha20poly1305" found. Am I looking at the wrong doc? |
On the topic of the original issue: I don't think it is likely .NET will include an in-box SSH client because SSH is extensible for algorithms, and the SSH use-case alone is not sufficient for the .NET team to provide these algorithms. Tmds.Ssh should now have a feature set that meets the needs of most .NET SSH use-cases. I'm interested in any feedback you have about the library (bugs, missing features, API, ...). To give feedback, you can open an issue in the repo or start a discussion. |
During the work with a customer, discovered that .NET core doesn't contain SSH which was crucial for the development of the engagement. Using a 3rd party NuGet package (known as SSH.NET) was the workaround, which unfortunately is not well maintained and supported, which brings the following issues:
Lack of more/new secure encryption algorithms given the fact the github repository lacks continuous support.
Current package is implemented using AMP (Asynchronous Programming Model), which is the legacy asynchronous implementation, compared to what most recent versions of dotnet .NET that supports – TAP (Task-based Asynchronous Pattern), which is what was used by customer.
It required to spend good amount of time reading SSH.NET repository, on how the package was implemented, look at some tests how those were written to have a good idea on how to consume the SSH client provided by the package.
it is very common to find SSH as a standard mechanism to communicate between services across different platform, which brings the issue here whether this should be considered as workable item.
The text was updated successfully, but these errors were encountered: