├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── ByteReader.cs ├── ByteWriter.cs ├── Ciphers ├── ICipher.cs ├── NoCipher.cs └── TripleDESCBC.cs ├── Client.cs ├── Compressions ├── ICompression.cs └── NoCompression.cs ├── ExchangeContext.cs ├── HostKeyAlgorithms ├── IHostKeyAlgorithm.cs └── SSHRSA.cs ├── IAlgorithm.cs ├── KSSHServerException.cs ├── KexAlgorithms ├── DiffieHellmanGroup14SHA1.cs └── IKexAlgorithm.cs ├── LICENSE ├── MACAlgorithms ├── HMACSHA1.cs └── IMACAlgorithm.cs ├── Packets ├── DisconnectReason.cs ├── KexDHInit.cs ├── KexDHReply.cs ├── KexInit.cs ├── NewKeys.cs ├── Packet.cs ├── PacketType.cs └── Unimplemented.cs ├── Program.cs ├── README.md ├── Server.cs ├── obj ├── project.assets.json ├── sshserver.csproj.nuget.cache ├── sshserver.csproj.nuget.g.props └── sshserver.csproj.nuget.g.targets ├── sshserver.csproj ├── sshserver.json └── sshserver.sln /.gitignore: -------------------------------------------------------------------------------- 1 | obj/Debug/netcoreapp2.0/sshserver.csproj.FileListAbsolute.txt 2 | bin/Debug/netcoreapp2.0/sshserver.dll 3 | bin/Debug/netcoreapp2.0/sshserver.pdb 4 | bin/Debug/netcoreapp2.0/sshserver.deps.json 5 | bin/Debug/netcoreapp2.0/sshserver.runtimeconfig.json 6 | bin/Debug/netcoreapp2.0/sshserver.runtimeconfig.dev.json 7 | obj/project.assets.json 8 | obj/sshserver.csproj.nuget.cache 9 | obj/Debug/netcoreapp2.0/sshserver.dll 10 | obj/Debug/netcoreapp2.0/sshserver.pdb 11 | obj/Debug/netcoreapp2.0/sshserver.AssemblyInfo.cs 12 | obj/Debug/netcoreapp2.0/sshserver.AssemblyInfoInputs.cache 13 | obj/Debug/netcoreapp2.0/sshserver.csproj.CoreCompileInputs.cache 14 | obj/project.assets.json 15 | obj/sshserver.csproj.nuget.cache 16 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp2.0/sshserver.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false, 19 | "internalConsoleOptions": "openOnSessionStart" 20 | }, 21 | { 22 | "name": ".NET Core Attach", 23 | "type": "coreclr", 24 | "request": "attach", 25 | "processId": "${command:pickProcess}" 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "taskName": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/sshserver.csproj" 11 | ], 12 | "problemMatcher": "$msCompile" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /ByteReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace KSSHServer 9 | { 10 | public class ByteReader : IDisposable 11 | { 12 | private readonly char[] _ListSeparator = new char[] {','}; 13 | private MemoryStream _Stream; 14 | 15 | public bool IsEOF 16 | { 17 | get 18 | { 19 | if(disposedValue) 20 | throw new ObjectDisposedException("ByteReader"); 21 | 22 | return _Stream.Position == _Stream.Length; 23 | } 24 | 25 | private set {} 26 | } 27 | 28 | public ByteReader(byte[] data) 29 | { 30 | _Stream = new MemoryStream(data); 31 | } 32 | 33 | public byte[] GetBytes(int length) 34 | { 35 | if(disposedValue) 36 | throw new ObjectDisposedException("ByteReader"); 37 | 38 | byte[] data = new byte[length]; 39 | _Stream.Read(data, 0, length); 40 | 41 | return data; 42 | } 43 | 44 | public byte[] GetMPInt() 45 | { 46 | UInt32 size = GetUInt32(); 47 | 48 | if(size == 0) 49 | return new byte[1]; 50 | 51 | byte[] data = GetBytes((int) size); 52 | 53 | if (data[0] == 0) 54 | return data.Skip(1).ToArray(); 55 | 56 | return data; 57 | } 58 | 59 | public UInt32 GetUInt32() 60 | { 61 | byte[] data = GetBytes(4); // 4 bytes = UInt32 62 | 63 | if(BitConverter.IsLittleEndian) 64 | data = data.Reverse().ToArray(); 65 | 66 | return BitConverter.ToUInt32(data, 0); 67 | } 68 | 69 | public string GetString() 70 | { 71 | return GetString(Encoding.ASCII); 72 | } 73 | 74 | public string GetString(Encoding encoding) 75 | { 76 | int length = (int)GetUInt32(); 77 | 78 | if (length == 0) 79 | return string.Empty; 80 | 81 | return encoding.GetString(GetBytes(length)); 82 | } 83 | 84 | public List GetNameList() 85 | { 86 | return new List(GetString().Split(_ListSeparator, StringSplitOptions.RemoveEmptyEntries)); 87 | } 88 | 89 | public bool GetBoolean() 90 | { 91 | return (GetByte() != 0); 92 | } 93 | 94 | public byte GetByte() 95 | { 96 | if(disposedValue) 97 | throw new ObjectDisposedException("ByteReader"); 98 | 99 | return (byte)_Stream.ReadByte(); 100 | } 101 | 102 | #region IDisposable Support 103 | private bool disposedValue = false; // To detect redundant calls 104 | 105 | protected virtual void Dispose(bool disposing) 106 | { 107 | if (!disposedValue) 108 | { 109 | if (disposing) 110 | { 111 | _Stream.Dispose(); 112 | _Stream = null; 113 | } 114 | 115 | disposedValue = true; 116 | } 117 | } 118 | 119 | void IDisposable.Dispose() 120 | { 121 | // Do not change this code. Put cleanup code in Dispose(bool disposing) above. 122 | Dispose(true); 123 | } 124 | #endregion 125 | } 126 | } -------------------------------------------------------------------------------- /ByteWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace KSSHServer 10 | { 11 | public class ByteWriter : IDisposable 12 | { 13 | private MemoryStream _Stream = new MemoryStream(); 14 | 15 | public void WritePacketType(Packets.PacketType packetType) 16 | { 17 | WriteByte((byte)packetType); 18 | } 19 | 20 | public void WriteByte(byte value) 21 | { 22 | if (disposedValue) 23 | throw new ObjectDisposedException("ByteWriter"); 24 | 25 | _Stream.WriteByte(value); 26 | } 27 | 28 | public void WriteBytes(byte[] data) 29 | { 30 | WriteUInt32((uint)data.Count()); 31 | WriteRawBytes(data); 32 | 33 | } 34 | 35 | public void WriteString(string data) 36 | { 37 | WriteString(data, Encoding.ASCII); 38 | } 39 | public void WriteString(string data, Encoding encoding) 40 | { 41 | WriteBytes(encoding.GetBytes(data)); 42 | } 43 | 44 | public void WriteStringList(IEnumerable list) 45 | { 46 | WriteString(string.Join(",", list)); 47 | } 48 | 49 | public void WriteUInt32(uint data) 50 | { 51 | byte[] buffer = BitConverter.GetBytes(data); 52 | 53 | if (BitConverter.IsLittleEndian) 54 | buffer = buffer.Reverse().ToArray(); 55 | 56 | WriteRawBytes(buffer); 57 | } 58 | 59 | public void WriteMPInt(byte[] value) 60 | { 61 | if ((value.Length == 1) && (value[0] == 0)) 62 | { 63 | WriteUInt32(0); 64 | return; 65 | } 66 | 67 | uint length = (uint)value.Length; 68 | if ((value[0] & 0x80) != 0) 69 | { 70 | WriteUInt32((uint)length + 1); 71 | WriteByte(0x00); 72 | } 73 | else 74 | { 75 | WriteUInt32((uint)length); 76 | } 77 | 78 | WriteRawBytes(value); 79 | } 80 | 81 | public void WriteRawBytes(byte[] value) 82 | { 83 | if (disposedValue) 84 | throw new ObjectDisposedException("ByteWriter"); 85 | 86 | _Stream.Write(value, 0, value.Count()); 87 | } 88 | 89 | public byte[] ToByteArray() 90 | { 91 | if (disposedValue) 92 | throw new ObjectDisposedException("ByteWriter"); 93 | 94 | return _Stream.ToArray(); 95 | } 96 | 97 | #region IDisposable Support 98 | private bool disposedValue = false; // To detect redundant calls 99 | 100 | protected virtual void Dispose(bool disposing) 101 | { 102 | if (!disposedValue) 103 | { 104 | if (disposing) 105 | { 106 | _Stream.Dispose(); 107 | _Stream = null; 108 | } 109 | 110 | disposedValue = true; 111 | } 112 | } 113 | 114 | public void Dispose() 115 | { 116 | // Do not change this code. Put cleanup code in Dispose(bool disposing) above. 117 | Dispose(true); 118 | } 119 | #endregion 120 | } 121 | } -------------------------------------------------------------------------------- /Ciphers/ICipher.cs: -------------------------------------------------------------------------------- 1 | namespace KSSHServer.Ciphers 2 | { 3 | public interface ICipher : IAlgorithm 4 | { 5 | uint BlockSize { get; } 6 | uint KeySize { get; } 7 | byte[] Encrypt(byte[] data); 8 | byte[] Decrypt(byte[] data); 9 | void SetKey(byte[] key, byte[] iv); 10 | } 11 | } -------------------------------------------------------------------------------- /Ciphers/NoCipher.cs: -------------------------------------------------------------------------------- 1 | namespace KSSHServer.Ciphers 2 | { 3 | public class NoCipher : ICipher 4 | { 5 | public uint BlockSize 6 | { 7 | get 8 | { 9 | return 8; 10 | } 11 | } 12 | 13 | public uint KeySize 14 | { 15 | get 16 | { 17 | return 0; 18 | } 19 | } 20 | 21 | public string Name 22 | { 23 | get 24 | { 25 | return "none"; 26 | } 27 | } 28 | 29 | public byte[] Decrypt(byte[] data) 30 | { 31 | return data; 32 | } 33 | 34 | public byte[] Encrypt(byte[] data) 35 | { 36 | return data; 37 | } 38 | 39 | public void SetKey(byte[] key, byte[] iv) 40 | { 41 | // No key for this Cipher 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Ciphers/TripleDESCBC.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Security.Cryptography; 4 | using KSSHServer.Packets; 5 | 6 | namespace KSSHServer.Ciphers 7 | { 8 | public class TripleDESCBC : ICipher 9 | { 10 | private TripleDES _3DES = TripleDES.Create(); 11 | private ICryptoTransform _Encryptor; 12 | private ICryptoTransform _Decryptor; 13 | public uint BlockSize 14 | { 15 | get 16 | { 17 | // TripleDES.BlockSize is the size of the block in bits, so we need to divide by 8 18 | // to convert from bits to bytes. 19 | return (uint)(_3DES.BlockSize / 8); 20 | } 21 | } 22 | 23 | public uint KeySize 24 | { 25 | get 26 | { 27 | // TripleDES.KeySize is the size of the key in bits, so we need to divide by 8 28 | // to convert from bits to bytes. 29 | return (uint)(_3DES.KeySize / 8); 30 | } 31 | } 32 | 33 | public string Name 34 | { 35 | get 36 | { 37 | return "3des-cbc"; 38 | } 39 | } 40 | 41 | public TripleDESCBC() 42 | { 43 | _3DES.KeySize = 192; 44 | _3DES.Padding = PaddingMode.None; 45 | _3DES.Mode = CipherMode.CBC; 46 | } 47 | 48 | public byte[] Decrypt(byte[] data) 49 | { 50 | return PerformTransform(_Decryptor, data); 51 | } 52 | 53 | public byte[] Encrypt(byte[] data) 54 | { 55 | return PerformTransform(_Encryptor, data); 56 | } 57 | 58 | private byte[] PerformTransform(ICryptoTransform transform, byte[] data) 59 | { 60 | if (transform == null) 61 | throw new KSSHServerException(DisconnectReason.SSH_DISCONNECT_KEY_EXCHANGE_FAILED, "SetKey must be called before attempting to encrypt or decrypt data."); 62 | 63 | var output = new byte[data.Length]; 64 | transform.TransformBlock(data, 0, data.Length, output, 0); 65 | 66 | return output; 67 | } 68 | 69 | public void SetKey(byte[] key, byte[] iv) 70 | { 71 | _3DES.Key = key; 72 | _3DES.IV = iv; 73 | 74 | _Decryptor = _3DES.CreateDecryptor(key, iv); 75 | _Encryptor = _3DES.CreateEncryptor(key, iv); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /Client.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Text; 7 | using KSSHServer.KexAlgorithms; 8 | using KSSHServer.Packets; 9 | using Microsoft.CSharp.RuntimeBinder; 10 | using Microsoft.Extensions.Logging; 11 | using System.Threading; 12 | using System.Security.Cryptography; 13 | using System.Linq; 14 | using KSSHServer.Packets.SSHServer.Packets; 15 | 16 | namespace KSSHServer 17 | { 18 | public class Client 19 | { 20 | private ILogger _Logger; 21 | private Socket _Socket; 22 | private bool _ProtocolVersionExchangeComplete = false; 23 | private string _ProtocolVersionExchange; 24 | private Packets.KexInit _KexInitServerToClient = new Packets.KexInit(); 25 | private Packets.KexInit _KexInitClientToServer = null; 26 | private ExchangeContext _ActiveExchangeContext = new ExchangeContext(); 27 | private ExchangeContext _PendingExchangeContext = new ExchangeContext(); 28 | private byte[] _SessionId = null; 29 | private int _CurrentSentPacketNumber = -1; 30 | private int _CurrentReceivedPacketNumber = -1; 31 | private long _TotalBytesTransferred = 0; 32 | private DateTime _KeyTimeout = DateTime.UtcNow.AddHours(1); 33 | 34 | public Client(Socket socket, ILogger logger) 35 | { 36 | _Socket = socket; 37 | _Logger = logger; 38 | 39 | _KexInitServerToClient.KexAlgorithms.AddRange(Server.GetNames(Server.SupportedKexAlgorithms)); 40 | _KexInitServerToClient.ServerHostKeyAlgorithms.AddRange(Server.GetNames(Server.SupportedHostKeyAlgorithms)); 41 | _KexInitServerToClient.EncryptionAlgorithmsClientToServer.AddRange(Server.GetNames(Server.SupportedCiphers)); 42 | _KexInitServerToClient.EncryptionAlgorithmsServerToClient.AddRange(Server.GetNames(Server.SupportedCiphers)); 43 | _KexInitServerToClient.MacAlgorithmsClientToServer.AddRange(Server.GetNames(Server.SupportedMACAlgorithms)); 44 | _KexInitServerToClient.MacAlgorithmsServerToClient.AddRange(Server.GetNames(Server.SupportedMACAlgorithms)); 45 | _KexInitServerToClient.CompressionAlgorithmsClientToServer.AddRange(Server.GetNames(Server.SupportedCompressions)); 46 | _KexInitServerToClient.CompressionAlgorithmsServerToClient.AddRange(Server.GetNames(Server.SupportedCompressions)); 47 | 48 | const int socketBufferSize = 2 * Packets.Packet.MaxPacketSize; 49 | _Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, socketBufferSize); 50 | _Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, socketBufferSize); 51 | _Socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); 52 | _Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true); 53 | 54 | Send($"{ServerConstants.ProtocolVersionExchange}\r\n"); 55 | 56 | // 7.1. Algorithm Negotiation - https://tools.ietf.org/html/rfc4253#section-7.1 57 | Send(_KexInitServerToClient); 58 | } 59 | 60 | private void Send(Packet packet) 61 | { 62 | packet.PacketSequence = GetSentPacketNumber(); 63 | 64 | byte[] payload = _ActiveExchangeContext.CompressionServerToClient.Compress(packet.GetBytes()); 65 | 66 | uint blockSize = _ActiveExchangeContext.CipherServerToClient.BlockSize; 67 | 68 | byte paddingLength = (byte)(blockSize - (payload.Length + 5) % blockSize); 69 | if (paddingLength < 4) 70 | paddingLength += (byte)blockSize; 71 | 72 | byte[] padding = new byte[paddingLength]; 73 | RandomNumberGenerator.Create().GetBytes(padding); 74 | 75 | uint packetLength = (uint)(payload.Length + paddingLength + 1); 76 | 77 | using (ByteWriter writer = new ByteWriter()) 78 | { 79 | writer.WriteUInt32(packetLength); 80 | writer.WriteByte(paddingLength); 81 | writer.WriteRawBytes(payload); 82 | writer.WriteRawBytes(padding); 83 | 84 | payload = writer.ToByteArray(); 85 | } 86 | 87 | byte[] encryptedPayload = _ActiveExchangeContext.CipherServerToClient.Encrypt(payload); 88 | if (_ActiveExchangeContext.MACAlgorithmServerToClient != null) 89 | { 90 | byte[] mac = _ActiveExchangeContext.MACAlgorithmServerToClient.ComputeHash(packet.PacketSequence, payload); 91 | encryptedPayload = encryptedPayload.Concat(mac).ToArray(); 92 | } 93 | 94 | Send(encryptedPayload); 95 | this.ConsiderReExchange(); 96 | } 97 | 98 | private void Send(string message) 99 | { 100 | _Logger.LogDebug($"Sending raw string: {message.Trim()}"); 101 | Send(Encoding.UTF8.GetBytes(message)); 102 | } 103 | 104 | private void Send(byte[] message) 105 | { 106 | if (!IsConnected()) 107 | return; 108 | 109 | // Increase bytes transferred 110 | _TotalBytesTransferred += message.Length; 111 | 112 | _Socket.Send(message); 113 | } 114 | 115 | public bool IsConnected() 116 | { 117 | return (_Socket != null); 118 | } 119 | 120 | public void Poll() 121 | { 122 | if (!IsConnected()) 123 | return; 124 | 125 | bool dataAvailable = _Socket.Poll(0, SelectMode.SelectRead); 126 | 127 | if (dataAvailable) 128 | { 129 | int read = _Socket.Available; 130 | 131 | if (read < 1) 132 | { 133 | Disconnect(DisconnectReason.SSH_DISCONNECT_CONNECTION_LOST, "The client disconnected."); 134 | return; 135 | } 136 | 137 | if (!_ProtocolVersionExchangeComplete) 138 | { 139 | try 140 | { 141 | ReadProtocolVersionExchange(); 142 | 143 | if (_ProtocolVersionExchangeComplete) 144 | { 145 | _Logger.LogDebug($"Received ProtocolVersionExchange:{_ProtocolVersionExchange}"); 146 | ValidateProtocolVersionExchange(); 147 | } 148 | } 149 | catch (System.Exception) 150 | { 151 | Disconnect(DisconnectReason.SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED, "Failed to get the protocol version exchange."); 152 | return; 153 | } 154 | } 155 | 156 | if (_ProtocolVersionExchangeComplete) 157 | { 158 | try 159 | { 160 | Packets.Packet packet = ReadPacket(); 161 | 162 | while (packet != null) 163 | { 164 | _Logger.LogDebug($"Received Packet: {packet.PacketType}"); 165 | 166 | // Handle specific packet 167 | HandlePacket(packet); 168 | 169 | // Read next packet 170 | packet = ReadPacket(); 171 | } 172 | 173 | ConsiderReExchange(); 174 | } 175 | catch (KSSHServerException ex) 176 | { 177 | _Logger.LogError(ex.Message); 178 | Disconnect(ex.Reason, ex.Message); 179 | return; 180 | } 181 | } 182 | } 183 | } 184 | 185 | private uint GetSentPacketNumber() 186 | { 187 | return (uint)Interlocked.Increment(ref _CurrentSentPacketNumber); 188 | } 189 | 190 | private uint GetReceivedPacketNumber() 191 | { 192 | return (uint)Interlocked.Increment(ref _CurrentReceivedPacketNumber); 193 | } 194 | 195 | // Read 1 byte from the socket until \r\n 196 | private void ReadProtocolVersionExchange() 197 | { 198 | NetworkStream stream = new NetworkStream(_Socket, false); 199 | string result = null; 200 | 201 | List data = new List(); 202 | 203 | bool foundCR = false; 204 | int val = stream.ReadByte(); 205 | 206 | while (val != -1) 207 | { 208 | if (foundCR && (val == '\n')) 209 | { 210 | result = Encoding.UTF8.GetString(data.ToArray()); 211 | _ProtocolVersionExchangeComplete = true; 212 | break; 213 | } 214 | 215 | if (val == '\r') 216 | { 217 | foundCR = true; 218 | } 219 | else 220 | { 221 | foundCR = false; 222 | data.Add((byte)val); 223 | } 224 | 225 | val = stream.ReadByte(); 226 | } 227 | 228 | _ProtocolVersionExchange += result; 229 | } 230 | 231 | private void HandlePacket(Packet packet) 232 | { 233 | try 234 | { 235 | HandleSpecificPacket((dynamic)packet); 236 | } 237 | catch (RuntimeBinderException) 238 | { 239 | _Logger.LogWarning($"Unhandled packet type: {packet.PacketType}"); 240 | 241 | Unimplemented unimplemented = new Unimplemented() 242 | { 243 | RejectedPacketNumber = packet.PacketSequence 244 | }; 245 | 246 | Send(unimplemented); 247 | } 248 | } 249 | 250 | private void HandleSpecificPacket(KexDHInit packet) 251 | { 252 | _Logger.LogDebug("Received KexDHInit"); 253 | 254 | if ((_PendingExchangeContext == null) || (_PendingExchangeContext.KexAlgorithm == null)) 255 | { 256 | throw new KSSHServerException(DisconnectReason.SSH_DISCONNECT_PROTOCOL_ERROR, "Server did not receive SSH_MSG_KEX_INIT as expected."); 257 | } 258 | 259 | // 1. C generates a random number x (1 < x < q) and computes e = g ^ x mod p. C sends e to S. 260 | // 2. S receives e. It computes K = e^y mod p 261 | byte[] sharedSecret = _PendingExchangeContext.KexAlgorithm.DecryptKeyExchange(packet.ClientValue); 262 | 263 | // 2. S generates a random number y (0 < y < q) and computes f = g ^ y mod p. 264 | byte[] serverKeyExchange = _PendingExchangeContext.KexAlgorithm.CreateKeyExchange(); 265 | 266 | byte[] hostKey = _PendingExchangeContext.HostKeyAlgorithm.CreateKeyAndCertificatesData(); 267 | 268 | // H = hash(V_C || V_S || I_C || I_S || K_S || e || f || K) 269 | byte[] exchangeHash = ComputeExchangeHash( 270 | _PendingExchangeContext.KexAlgorithm, 271 | hostKey, 272 | packet.ClientValue, 273 | serverKeyExchange, 274 | sharedSecret); 275 | 276 | if (_SessionId == null) 277 | _SessionId = exchangeHash; 278 | 279 | // https://tools.ietf.org/html/rfc4253#section-7.2 280 | 281 | // Initial IV client to server: HASH(K || H || "A" || session_id) 282 | // (Here K is encoded as mpint and "A" as byte and session_id as raw 283 | // data. "A" means the single character A, ASCII 65). 284 | byte[] clientCipherIV = ComputeEncryptionKey( 285 | _PendingExchangeContext.KexAlgorithm, 286 | exchangeHash, 287 | _PendingExchangeContext.CipherClientToServer.BlockSize, 288 | sharedSecret, 'A'); 289 | 290 | // Initial IV server to client: HASH(K || H || "B" || session_id) 291 | byte[] serverCipherIV = ComputeEncryptionKey( 292 | _PendingExchangeContext.KexAlgorithm, 293 | exchangeHash, 294 | _PendingExchangeContext.CipherServerToClient.BlockSize, 295 | sharedSecret, 'B'); 296 | 297 | // Encryption key client to server: HASH(K || H || "C" || session_id) 298 | byte[] clientCipherKey = ComputeEncryptionKey( 299 | _PendingExchangeContext.KexAlgorithm, 300 | exchangeHash, 301 | _PendingExchangeContext.CipherClientToServer.KeySize, 302 | sharedSecret, 'C'); 303 | 304 | // Encryption key server to client: HASH(K || H || "D" || session_id) 305 | byte[] serverCipherKey = ComputeEncryptionKey( 306 | _PendingExchangeContext.KexAlgorithm, 307 | exchangeHash, 308 | _PendingExchangeContext.CipherServerToClient.KeySize, 309 | sharedSecret, 'D'); 310 | 311 | // Integrity key client to server: HASH(K || H || "E" || session_id) 312 | byte[] clientHmacKey = ComputeEncryptionKey( 313 | _PendingExchangeContext.KexAlgorithm, 314 | exchangeHash, 315 | _PendingExchangeContext.MACAlgorithmClientToServer.KeySize, 316 | sharedSecret, 'E'); 317 | 318 | // Integrity key server to client: HASH(K || H || "F" || session_id) 319 | byte[] serverHmacKey = ComputeEncryptionKey( 320 | _PendingExchangeContext.KexAlgorithm, 321 | exchangeHash, 322 | _PendingExchangeContext.MACAlgorithmServerToClient.KeySize, 323 | sharedSecret, 'F'); 324 | 325 | // Set all keys we just generated 326 | _PendingExchangeContext.CipherClientToServer.SetKey(clientCipherKey, clientCipherIV); 327 | _PendingExchangeContext.CipherServerToClient.SetKey(serverCipherKey, serverCipherIV); 328 | _PendingExchangeContext.MACAlgorithmClientToServer.SetKey(clientHmacKey); 329 | _PendingExchangeContext.MACAlgorithmServerToClient.SetKey(serverHmacKey); 330 | 331 | // Send reply to client! 332 | KexDHReply reply = new KexDHReply() 333 | { 334 | ServerHostKey = hostKey, 335 | ServerValue = serverKeyExchange, 336 | Signature = _PendingExchangeContext.HostKeyAlgorithm.CreateSignatureData(exchangeHash) 337 | }; 338 | 339 | Send(reply); 340 | Send(new NewKeys()); 341 | } 342 | 343 | private void HandleSpecificPacket(KexInit packet) 344 | { 345 | _Logger.LogDebug("Received KexInit packet."); 346 | 347 | if (_PendingExchangeContext == null) 348 | { 349 | _Logger.LogDebug("Re-exchanging keys!"); 350 | _PendingExchangeContext = new ExchangeContext(); 351 | Send(_KexInitServerToClient); 352 | } 353 | 354 | _KexInitClientToServer = packet; 355 | 356 | _PendingExchangeContext.KexAlgorithm = packet.PickKexAlgorithm(); 357 | _PendingExchangeContext.HostKeyAlgorithm = packet.PickHostKeyAlgorithm(); 358 | _PendingExchangeContext.CipherClientToServer = packet.PickCipherClientToServer(); 359 | _PendingExchangeContext.CipherServerToClient = packet.PickCipherServerToClient(); 360 | _PendingExchangeContext.MACAlgorithmClientToServer = packet.PickMACAlgorithmClientToServer(); 361 | _PendingExchangeContext.MACAlgorithmServerToClient = packet.PickMACAlgorithmServerToClient(); 362 | _PendingExchangeContext.CompressionClientToServer = packet.PickCompressionAlgorithmClientToServer(); 363 | _PendingExchangeContext.CompressionServerToClient = packet.PickCompressionAlgorithmServerToClient(); 364 | 365 | _Logger.LogDebug($"Selected KexAlgorithm: {_PendingExchangeContext.KexAlgorithm.Name}"); 366 | _Logger.LogDebug($"Selected HostKeyAlgorithm: {_PendingExchangeContext.HostKeyAlgorithm.Name}"); 367 | _Logger.LogDebug($"Selected CipherClientToServer: {_PendingExchangeContext.CipherClientToServer.Name}"); 368 | _Logger.LogDebug($"Selected CipherServerToClient: {_PendingExchangeContext.CipherServerToClient.Name}"); 369 | _Logger.LogDebug($"Selected MACAlgorithmClientToServer: {_PendingExchangeContext.MACAlgorithmClientToServer.Name}"); 370 | _Logger.LogDebug($"Selected MACAlgorithmServerToClient: {_PendingExchangeContext.MACAlgorithmServerToClient.Name}"); 371 | _Logger.LogDebug($"Selected CompressionClientToServer: {_PendingExchangeContext.CompressionClientToServer.Name}"); 372 | _Logger.LogDebug($"Selected CompressionServerToClient: {_PendingExchangeContext.CompressionServerToClient.Name}"); 373 | } 374 | 375 | private void HandleSpecificPacket(NewKeys packet) 376 | { 377 | _Logger.LogDebug("Received NewKeys"); 378 | 379 | _ActiveExchangeContext = _PendingExchangeContext; 380 | _PendingExchangeContext = null; 381 | 382 | 383 | // Reset re-exchange values 384 | _TotalBytesTransferred = 0; 385 | _KeyTimeout = DateTime.UtcNow.AddHours(1); 386 | } 387 | 388 | private byte[] ComputeExchangeHash(IKexAlgorithm kexAlgorithm, byte[] hostKeyAndCerts, byte[] clientExchangeValue, byte[] serverExchangeValue, byte[] sharedSecret) 389 | { 390 | // H = hash(V_C || V_S || I_C || I_S || K_S || e || f || K) 391 | using (ByteWriter writer = new ByteWriter()) 392 | { 393 | writer.WriteString(_ProtocolVersionExchange); 394 | writer.WriteString(ServerConstants.ProtocolVersionExchange); 395 | 396 | writer.WriteBytes(_KexInitClientToServer.GetBytes()); 397 | writer.WriteBytes(_KexInitServerToClient.GetBytes()); 398 | writer.WriteBytes(hostKeyAndCerts); 399 | 400 | writer.WriteMPInt(clientExchangeValue); 401 | writer.WriteMPInt(serverExchangeValue); 402 | writer.WriteMPInt(sharedSecret); 403 | 404 | return kexAlgorithm.ComputeHash(writer.ToByteArray()); 405 | } 406 | } 407 | 408 | private byte[] ComputeEncryptionKey(IKexAlgorithm kexAlgorithm, byte[] exchangeHash, uint keySize, byte[] sharedSecret, char letter) 409 | { 410 | // K(X) = HASH(K || H || X || session_id) 411 | 412 | // Prepare the buffer 413 | byte[] keyBuffer = new byte[keySize]; 414 | int keyBufferIndex = 0; 415 | int currentHashLength = 0; 416 | byte[] currentHash = null; 417 | 418 | // We can stop once we fill the key buffer 419 | while (keyBufferIndex < keySize) 420 | { 421 | using (ByteWriter writer = new ByteWriter()) 422 | { 423 | // Write "K" 424 | writer.WriteMPInt(sharedSecret); 425 | 426 | // Write "H" 427 | writer.WriteRawBytes(exchangeHash); 428 | 429 | if (currentHash == null) 430 | { 431 | // If we haven't done this yet, add the "X" and session_id 432 | writer.WriteByte((byte)letter); 433 | writer.WriteRawBytes(_SessionId); 434 | } 435 | else 436 | { 437 | // If the key isn't long enough after the first pass, we need to 438 | // write the current hash as described here: 439 | // K1 = HASH(K || H || X || session_id) (X is e.g., "A") 440 | // K2 = HASH(K || H || K1) 441 | // K3 = HASH(K || H || K1 || K2) 442 | // ... 443 | // key = K1 || K2 || K3 || ... 444 | writer.WriteRawBytes(currentHash); 445 | } 446 | 447 | currentHash = kexAlgorithm.ComputeHash(writer.ToByteArray()); 448 | } 449 | 450 | currentHashLength = Math.Min(currentHash.Length, (int)(keySize - keyBufferIndex)); 451 | Array.Copy(currentHash, 0, keyBuffer, keyBufferIndex, currentHashLength); 452 | 453 | keyBufferIndex += currentHashLength; 454 | } 455 | 456 | return keyBuffer; 457 | } 458 | 459 | public Packet ReadPacket() 460 | { 461 | if (_Socket == null) 462 | return null; 463 | 464 | uint blockSize = _ActiveExchangeContext.CipherClientToServer.BlockSize; 465 | 466 | // We must have at least 1 block to read 467 | if (_Socket.Available < blockSize) 468 | return null; // Packet not here 469 | 470 | byte[] firstBlock = new byte[blockSize]; 471 | int bytesRead = _Socket.Receive(firstBlock); 472 | if (bytesRead != blockSize) 473 | throw new KSSHServerException(DisconnectReason.SSH_DISCONNECT_CONNECTION_LOST, "Failed to read from socket."); 474 | 475 | firstBlock = _ActiveExchangeContext.CipherClientToServer.Decrypt(firstBlock); 476 | 477 | uint packetLength = 0; 478 | byte paddingLength = 0; 479 | using (ByteReader reader = new ByteReader(firstBlock)) 480 | { 481 | // uint32 packet_length 482 | // packet_length 483 | // The length of the packet in bytes, not including 'mac' or the 484 | // 'packet_length' field itself. 485 | packetLength = reader.GetUInt32(); 486 | if (packetLength > Packet.MaxPacketSize) 487 | throw new KSSHServerException(DisconnectReason.SSH_DISCONNECT_PROTOCOL_ERROR, $"Client tried to send a packet bigger than MaxPacketSize ({Packet.MaxPacketSize} bytes): {packetLength} bytes"); 488 | 489 | // byte padding_length 490 | // padding_length 491 | // Length of 'random padding' (bytes). 492 | paddingLength = reader.GetByte(); 493 | } 494 | 495 | // byte[n1] payload; n1 = packet_length - padding_length - 1 496 | // payload 497 | // The useful contents of the packet. If compression has been 498 | // negotiated, this field is compressed. Initially, compression 499 | // MUST be "none". 500 | uint bytesToRead = packetLength - blockSize + 4; 501 | 502 | byte[] restOfPacket = new byte[bytesToRead]; 503 | bytesRead = _Socket.Receive(restOfPacket); 504 | if (bytesRead != bytesToRead) 505 | throw new KSSHServerException(DisconnectReason.SSH_DISCONNECT_CONNECTION_LOST, "Failed to read from socket."); 506 | 507 | restOfPacket = _ActiveExchangeContext.CipherClientToServer.Decrypt(restOfPacket); 508 | 509 | uint payloadLength = packetLength - paddingLength - 1; 510 | byte[] fullPacket = firstBlock.Concat(restOfPacket).ToArray(); 511 | 512 | // Track total bytes read 513 | _TotalBytesTransferred += fullPacket.Length; 514 | 515 | byte[] payload = fullPacket.Skip(Packet._PacketHeaderSize).Take((int)(packetLength - paddingLength - 1)).ToArray(); 516 | 517 | // byte[n2] random padding; n2 = padding_length 518 | // random padding 519 | // Arbitrary-length padding, such that the total length of 520 | // (packet_length || padding_length || payload || random padding) 521 | // is a multiple of the cipher block size or 8, whichever is 522 | // larger. There MUST be at least four bytes of padding. The 523 | // padding SHOULD consist of random bytes. The maximum amount of 524 | // padding is 255 bytes. 525 | 526 | // byte[m] mac (Message Authentication Code - MAC); m = mac_length 527 | // mac 528 | // Message Authentication Code. If message authentication has 529 | // been negotiated, this field contains the MAC bytes. Initially, 530 | // the MAC algorithm MUST be "none". 531 | 532 | uint packetNumber = GetReceivedPacketNumber(); 533 | if (_ActiveExchangeContext.MACAlgorithmClientToServer != null) 534 | { 535 | byte[] clientMac = new byte[_ActiveExchangeContext.MACAlgorithmClientToServer.DigestLength]; 536 | bytesRead = _Socket.Receive(clientMac); 537 | if (bytesRead != _ActiveExchangeContext.MACAlgorithmClientToServer.DigestLength) 538 | throw new KSSHServerException(DisconnectReason.SSH_DISCONNECT_CONNECTION_LOST, "Failed to read from socket."); 539 | 540 | var mac = _ActiveExchangeContext.MACAlgorithmClientToServer.ComputeHash(packetNumber, fullPacket); 541 | if (!clientMac.SequenceEqual(mac)) 542 | { 543 | throw new KSSHServerException(DisconnectReason.SSH_DISCONNECT_MAC_ERROR, "MAC from client is invalid"); 544 | } 545 | } 546 | 547 | payload = _ActiveExchangeContext.CompressionClientToServer.Decompress(payload); 548 | 549 | using (ByteReader packetReader = new ByteReader(payload)) 550 | { 551 | PacketType type = (PacketType)packetReader.GetByte(); 552 | 553 | if (Packet._PacketTypes.ContainsKey(type)) 554 | { 555 | 556 | Packet packet = Activator.CreateInstance(Packet._PacketTypes[type]) as Packet; 557 | packet.Load(packetReader); 558 | packet.PacketSequence = packetNumber; 559 | return packet; 560 | } 561 | 562 | _Logger.LogWarning($"Unimplemented packet type: {type}"); 563 | 564 | Unimplemented unimplemented = new Unimplemented() 565 | { 566 | RejectedPacketNumber = packetNumber 567 | }; 568 | 569 | Send(unimplemented); 570 | } 571 | 572 | return null; 573 | } 574 | 575 | private void ConsiderReExchange() 576 | { 577 | const long OneGB = (1024 * 1024 * 1024); 578 | if ((_TotalBytesTransferred > OneGB) || (_KeyTimeout < DateTime.UtcNow)) 579 | { 580 | // Time to get new keys! 581 | _TotalBytesTransferred = 0; 582 | _KeyTimeout = DateTime.UtcNow.AddHours(1); 583 | 584 | _Logger.LogDebug("Trigger re-exchange from server"); 585 | _PendingExchangeContext = new ExchangeContext(); 586 | Send(_KexInitServerToClient); 587 | } 588 | } 589 | 590 | private void ValidateProtocolVersionExchange() 591 | { 592 | // https://tools.ietf.org/html/rfc4253#section-4.2 593 | //SSH-protoversion-softwareversion SP comments 594 | 595 | string[] pveParts = _ProtocolVersionExchange.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); 596 | if (pveParts.Length == 0) 597 | throw new UnauthorizedAccessException("Invalid Protocol Version Exchange was received - No Data"); 598 | 599 | string[] versionParts = pveParts[0].Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries); 600 | if (versionParts.Length < 3) 601 | throw new UnauthorizedAccessException($"Invalid Protocol Version Exchange was received - Not enough dashes - {pveParts[0]}"); 602 | 603 | if (versionParts[1] != "2.0") 604 | throw new UnauthorizedAccessException($"Invalid Protocol Version Exchange was received - Unsupported Version - {versionParts[1]}"); 605 | 606 | // If we get here, all is well! 607 | } 608 | 609 | 610 | public void Disconnect(DisconnectReason reason, string message) 611 | { 612 | _Logger.LogDebug($"Disconnected - {reason} - {message}"); 613 | if (_Socket != null) 614 | { 615 | if (reason != DisconnectReason.None) 616 | { 617 | try 618 | { 619 | Disconnect disconnect = new Disconnect() 620 | { 621 | Reason = reason, 622 | Description = message 623 | }; 624 | Send(disconnect); 625 | } 626 | catch (Exception) { } 627 | } 628 | 629 | try 630 | { 631 | _Socket.Shutdown(SocketShutdown.Both); 632 | } 633 | catch (Exception) { } 634 | 635 | _Socket = null; 636 | } 637 | } 638 | }; 639 | } -------------------------------------------------------------------------------- /Compressions/ICompression.cs: -------------------------------------------------------------------------------- 1 | namespace KSSHServer.Compressions 2 | { 3 | public interface ICompression : IAlgorithm 4 | { 5 | byte[] Compress(byte[] data); 6 | byte[] Decompress(byte[] data); 7 | } 8 | } -------------------------------------------------------------------------------- /Compressions/NoCompression.cs: -------------------------------------------------------------------------------- 1 | namespace KSSHServer.Compressions 2 | { 3 | public class NoCompression : ICompression 4 | { 5 | public string Name 6 | { 7 | get 8 | { 9 | return "none"; 10 | } 11 | } 12 | 13 | public byte[] Compress(byte[] data) 14 | { 15 | return data; 16 | } 17 | 18 | public byte[] Decompress(byte[] data) 19 | { 20 | return data; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /ExchangeContext.cs: -------------------------------------------------------------------------------- 1 | using KSSHServer.Ciphers; 2 | using KSSHServer.Compressions; 3 | using KSSHServer.HostKeyAlgorithms; 4 | using KSSHServer.KexAlgorithms; 5 | using KSSHServer.MACAlgorithms; 6 | 7 | namespace KSSHServer 8 | { 9 | public class ExchangeContext 10 | { 11 | public IKexAlgorithm KexAlgorithm { get; set; } = null; 12 | public IHostKeyAlgorithm HostKeyAlgorithm { get; set; } = null; 13 | public ICipher CipherClientToServer { get; set; } = new NoCipher(); 14 | public ICipher CipherServerToClient { get; set; } = new NoCipher(); 15 | public IMACAlgorithm MACAlgorithmClientToServer { get; set; } = null; 16 | public IMACAlgorithm MACAlgorithmServerToClient { get; set; } = null; 17 | public ICompression CompressionClientToServer { get; set; } = new NoCompression(); 18 | public ICompression CompressionServerToClient { get; set; } = new NoCompression(); 19 | } 20 | } -------------------------------------------------------------------------------- /HostKeyAlgorithms/IHostKeyAlgorithm.cs: -------------------------------------------------------------------------------- 1 | namespace KSSHServer.HostKeyAlgorithms 2 | { 3 | public interface IHostKeyAlgorithm : IAlgorithm 4 | { 5 | void ImportKey(string keyXml); 6 | byte[] CreateKeyAndCertificatesData(); 7 | byte[] CreateSignatureData(byte[] hash); 8 | } 9 | } -------------------------------------------------------------------------------- /HostKeyAlgorithms/SSHRSA.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Security.Cryptography; 4 | using System.Xml; 5 | 6 | namespace KSSHServer.HostKeyAlgorithms 7 | { 8 | public class SSHRSA : IHostKeyAlgorithm 9 | { 10 | private readonly RSA _RSA = RSA.Create(); 11 | 12 | public string Name { get { return "ssh-rsa"; } } 13 | 14 | public byte[] CreateKeyAndCertificatesData() 15 | { 16 | RSAParameters param = _RSA.ExportParameters(false); 17 | 18 | using (ByteWriter writer = new ByteWriter()) 19 | { 20 | writer.WriteString(Name); 21 | writer.WriteMPInt(param.Exponent); 22 | writer.WriteMPInt(param.Modulus); 23 | return writer.ToByteArray(); 24 | } 25 | } 26 | 27 | public byte[] CreateSignatureData(byte[] hash) 28 | { 29 | using (ByteWriter writer = new ByteWriter()) 30 | { 31 | writer.WriteString(Name); 32 | writer.WriteBytes(_RSA.SignData(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1)); 33 | return writer.ToByteArray(); 34 | } 35 | } 36 | 37 | public void ImportKey(string keyXml) 38 | { 39 | XmlDocument doc = new XmlDocument(); 40 | doc.LoadXml(keyXml); 41 | 42 | XmlElement root = doc["RSAKeyValue"]; 43 | 44 | RSAParameters p = new RSAParameters() 45 | { 46 | Modulus = Convert.FromBase64String(root["Modulus"].InnerText), 47 | Exponent = Convert.FromBase64String(root["Exponent"].InnerText), 48 | P = Convert.FromBase64String(root["P"].InnerText), 49 | Q = Convert.FromBase64String(root["Q"].InnerText), 50 | DP = Convert.FromBase64String(root["DP"].InnerText), 51 | DQ = Convert.FromBase64String(root["DQ"].InnerText), 52 | InverseQ = Convert.FromBase64String(root["InverseQ"].InnerText), 53 | D = Convert.FromBase64String(root["D"].InnerText) 54 | }; 55 | 56 | _RSA.ImportParameters(p); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /IAlgorithm.cs: -------------------------------------------------------------------------------- 1 | namespace KSSHServer 2 | { 3 | public interface IAlgorithm 4 | { 5 | string Name { get; } 6 | } 7 | } -------------------------------------------------------------------------------- /KSSHServerException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using KSSHServer.Packets; 6 | 7 | namespace KSSHServer 8 | { 9 | public class KSSHServerException : Exception 10 | { 11 | public DisconnectReason Reason { get; set; } 12 | 13 | public KSSHServerException(DisconnectReason reason, string message) : base(message) 14 | { 15 | Reason = reason; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /KexAlgorithms/DiffieHellmanGroup14SHA1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Numerics; 6 | using System.Security.Cryptography; 7 | using System.Threading.Tasks; 8 | 9 | namespace KSSHServer.KexAlgorithms 10 | { 11 | public class DiffieHellmanGroup14SHA1 : IKexAlgorithm 12 | { 13 | // http://tools.ietf.org/html/rfc3526 - 2048-bit MODP Group 14 | private const string MODPGroup2048 = "00FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF"; 15 | private static readonly BigInteger s_P; 16 | private static readonly BigInteger s_G; 17 | 18 | private readonly BigInteger m_Y; 19 | 20 | private readonly SHA1 m_HashAlgorithm = SHA1.Create(); 21 | 22 | // The following steps are used to exchange a key. In this: 23 | // C is the client 24 | // S is the server 25 | // p is a large safe prime (RFC 3526) 26 | // g is a generator (RFC 3526) 27 | // For a subgroup of GF(p); q is the order of the subgroup; V_S is S's 28 | // identification string; V_C is C's identification string; K_S is S's 29 | // public host key; I_C is C's SSH_MSG_KEXINIT message and I_S is S's 30 | // SSH_MSG_KEXINIT message that have been exchanged before this part 31 | // begins. 32 | 33 | public string Name 34 | { 35 | get 36 | { 37 | return "diffie-hellman-group14-sha1"; 38 | } 39 | } 40 | 41 | static DiffieHellmanGroup14SHA1() 42 | { 43 | // p is a large safe prime (RFC 3526) 44 | s_P = BigInteger.Parse(MODPGroup2048, NumberStyles.HexNumber); 45 | 46 | // g is a generator (RFC 3526) 47 | s_G = new BigInteger(2); 48 | } 49 | 50 | public DiffieHellmanGroup14SHA1() 51 | { 52 | // 2. S generates a random number y (0 < y < q) 53 | var bytes = new byte[80]; // 80 * 8 = 640 bits 54 | RandomNumberGenerator.Create().GetBytes(bytes); 55 | m_Y = BigInteger.Abs(new BigInteger(bytes)); 56 | } 57 | 58 | public byte[] CreateKeyExchange() 59 | { 60 | // and computes: f = g ^ y mod p. 61 | BigInteger keyExchange = BigInteger.ModPow(s_G, m_Y, s_P); 62 | byte[] key = keyExchange.ToByteArray(); 63 | if (BitConverter.IsLittleEndian) 64 | key = key.Reverse().ToArray(); 65 | 66 | if ((key.Length > 1) && (key[0] == 0x00)) 67 | { 68 | key = key.Skip(1).ToArray(); 69 | } 70 | 71 | return key; 72 | } 73 | 74 | public byte[] DecryptKeyExchange(byte[] keyEx) 75 | { 76 | // https://tools.ietf.org/html/rfc4253#section-8 77 | // 1. C generates a random number x (1 < x < q) and computes 78 | // e = g ^ x mod p. C sends e to S. 79 | 80 | // S receives e. It computes K = e^y mod p, 81 | if (BitConverter.IsLittleEndian) 82 | keyEx = keyEx.Reverse().ToArray(); 83 | 84 | BigInteger e = new BigInteger(keyEx.Concat(new byte[] { 0 }).ToArray()); 85 | byte[] decrypted = BigInteger.ModPow(e, m_Y, s_P).ToByteArray(); 86 | if (BitConverter.IsLittleEndian) 87 | decrypted = decrypted.Reverse().ToArray(); 88 | 89 | if ((decrypted.Length > 1) && (decrypted[0] == 0x00)) 90 | { 91 | decrypted = decrypted.Skip(1).ToArray(); 92 | } 93 | 94 | return decrypted; 95 | } 96 | 97 | public byte[] ComputeHash(byte[] value) 98 | { 99 | // H = hash(V_C || V_S || I_C || I_S || K_S || e || f || K) 100 | return m_HashAlgorithm.ComputeHash(value); 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /KexAlgorithms/IKexAlgorithm.cs: -------------------------------------------------------------------------------- 1 | namespace KSSHServer.KexAlgorithms 2 | { 3 | public interface IKexAlgorithm : IAlgorithm 4 | { 5 | byte[] CreateKeyExchange(); 6 | byte[] DecryptKeyExchange(byte[] keyEx); 7 | byte[] ComputeHash(byte[] value); 8 | } 9 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Karan Kadam 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MACAlgorithms/HMACSHA1.cs: -------------------------------------------------------------------------------- 1 | using KSSHServer.Packets; 2 | 3 | namespace KSSHServer.MACAlgorithms 4 | { 5 | public class HMACSHA1 : IMACAlgorithm 6 | { 7 | System.Security.Cryptography.HMACSHA1 _HMAC = null; 8 | public uint KeySize 9 | { 10 | get 11 | { 12 | // https://tools.ietf.org/html/rfc4253#section-6.4 13 | // According to this, the KeySize is 20 14 | return 20; 15 | } 16 | } 17 | 18 | public uint DigestLength 19 | { 20 | get 21 | { 22 | // https://tools.ietf.org/html/rfc4253#section-6.4 23 | // According to this, the DigestLength is 20 24 | return 20; 25 | } 26 | } 27 | 28 | public string Name 29 | { 30 | get 31 | { 32 | return "hmac-sha1"; 33 | } 34 | } 35 | 36 | public byte[] ComputeHash(uint packetNumber, byte[] data) 37 | { 38 | if (_HMAC == null) 39 | throw new KSSHServerException(DisconnectReason.SSH_DISCONNECT_KEY_EXCHANGE_FAILED, "SetKey must be called before attempting to ComputeHash"); 40 | 41 | using (ByteWriter writer = new ByteWriter()) 42 | { 43 | writer.WriteUInt32(packetNumber); 44 | writer.WriteRawBytes(data); 45 | return _HMAC.ComputeHash(writer.ToByteArray()); 46 | } 47 | } 48 | 49 | public void SetKey(byte[] key) 50 | { 51 | _HMAC = new System.Security.Cryptography.HMACSHA1(key); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /MACAlgorithms/IMACAlgorithm.cs: -------------------------------------------------------------------------------- 1 | namespace KSSHServer.MACAlgorithms 2 | { 3 | public interface IMACAlgorithm : IAlgorithm 4 | { 5 | uint KeySize { get; } 6 | uint DigestLength { get; } 7 | void SetKey(byte[] key); 8 | byte[] ComputeHash(uint packetNumber, byte[] data); 9 | } 10 | } -------------------------------------------------------------------------------- /Packets/DisconnectReason.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace KSSHServer.Packets 8 | { 9 | public enum DisconnectReason : uint 10 | { 11 | None = 0, 12 | SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1, 13 | SSH_DISCONNECT_PROTOCOL_ERROR = 2, 14 | SSH_DISCONNECT_KEY_EXCHANGE_FAILED = 3, 15 | SSH_DISCONNECT_RESERVED = 4, 16 | SSH_DISCONNECT_MAC_ERROR = 5, 17 | SSH_DISCONNECT_COMPRESSION_ERROR = 6, 18 | SSH_DISCONNECT_SERVICE_NOT_AVAILABLE = 7, 19 | SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8, 20 | SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9, 21 | SSH_DISCONNECT_CONNECTION_LOST = 10, 22 | SSH_DISCONNECT_BY_APPLICATION = 11, 23 | SSH_DISCONNECT_TOO_MANY_CONNECTIONS = 12, 24 | SSH_DISCONNECT_AUTH_CANCELLED_BY_USER = 13, 25 | SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14, 26 | SSH_DISCONNECT_ILLEGAL_USER_NAME = 15, 27 | } 28 | 29 | public class Disconnect : Packet 30 | { 31 | public override PacketType PacketType 32 | { 33 | get 34 | { 35 | return PacketType.SSH_MSG_DISCONNECT; 36 | } 37 | } 38 | 39 | public DisconnectReason Reason { get; set; } 40 | public string Description { get; set; } 41 | public string Language { get; set; } = "en"; 42 | 43 | public override void Load(ByteReader reader) 44 | { 45 | Reason = (DisconnectReason)reader.GetUInt32(); 46 | Description = reader.GetString(Encoding.UTF8); 47 | if (!reader.IsEOF) 48 | Language = reader.GetString(); 49 | } 50 | 51 | protected override void InternalGetBytes(ByteWriter writer) 52 | { 53 | writer.WriteUInt32((uint)Reason); 54 | writer.WriteString(Description, Encoding.UTF8); 55 | writer.WriteString(Language); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Packets/KexDHInit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace KSSHServer.Packets 4 | { 5 | public class KexDHInit : Packet 6 | { 7 | public override PacketType PacketType 8 | { 9 | get 10 | { 11 | return PacketType.SSH_MSG_KEXDH_INIT; 12 | } 13 | } 14 | 15 | public byte[] ClientValue { get; private set; } 16 | 17 | protected override void InternalGetBytes(ByteWriter writer) 18 | { 19 | // Server never sends this 20 | throw new KSSHServerException(DisconnectReason.SSH_DISCONNECT_KEY_EXCHANGE_FAILED, "KSSH Server should never send a SSH_MSG_KEXDH_INIT message"); 21 | } 22 | 23 | public override void Load(ByteReader reader) 24 | { 25 | // First, the client sends the following: 26 | // byte SSH_MSG_KEXDH_INIT (handled by base class) 27 | // mpint e 28 | ClientValue = reader.GetMPInt(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Packets/KexDHReply.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace KSSHServer.Packets 4 | { 5 | public class KexDHReply : Packet 6 | { 7 | public override PacketType PacketType 8 | { 9 | get 10 | { 11 | return PacketType.SSH_MSG_KEXDH_REPLY; 12 | } 13 | } 14 | 15 | public byte[] ServerHostKey { get; set; } 16 | public byte[] ServerValue { get; set; } 17 | public byte[] Signature { get; set; } 18 | 19 | protected override void InternalGetBytes(ByteWriter writer) 20 | { 21 | // string server public host key and certificates(K_S) 22 | // mpint f 23 | // string signature of H 24 | writer.WriteBytes(ServerHostKey); 25 | writer.WriteMPInt(ServerValue); 26 | writer.WriteBytes(Signature); 27 | } 28 | 29 | public override void Load(ByteReader reader) 30 | { 31 | // Client never sends this! 32 | throw new KSSHServerException(DisconnectReason.SSH_DISCONNECT_KEY_EXCHANGE_FAILED, "KSSH Client should never send a SSH_MSG_KEXDH_REPLY message"); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Packets/KexInit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Security.Cryptography; 4 | using KSSHServer.Ciphers; 5 | using KSSHServer.Compressions; 6 | using KSSHServer.HostKeyAlgorithms; 7 | using KSSHServer.KexAlgorithms; 8 | using KSSHServer.MACAlgorithms; 9 | 10 | namespace KSSHServer.Packets 11 | { 12 | public class KexInit : Packet 13 | { 14 | public byte[] Cookie { get; set; } = new byte[16]; 15 | public List KexAlgorithms { get; private set; } = new List(); 16 | public List ServerHostKeyAlgorithms { get; private set; } = new List(); 17 | public List EncryptionAlgorithmsClientToServer { get; private set; } = new List(); 18 | public List EncryptionAlgorithmsServerToClient { get; private set; } = new List(); 19 | public List MacAlgorithmsClientToServer { get; private set; } = new List(); 20 | public List MacAlgorithmsServerToClient { get; private set; } = new List(); 21 | public List CompressionAlgorithmsClientToServer { get; private set; } = new List(); 22 | public List CompressionAlgorithmsServerToClient { get; private set; } = new List(); 23 | public List LanguagesClientToServer { get; private set; } = new List(); 24 | public List LanguagesServerToClient { get; private set; } = new List(); 25 | public bool FirstKexPacketFollows { get; set; } 26 | public KexInit() 27 | { 28 | RandomNumberGenerator.Create().GetBytes(Cookie); 29 | } 30 | 31 | public override PacketType PacketType 32 | { 33 | get 34 | { 35 | return PacketType.SSH_MSG_KEXINIT; 36 | } 37 | } 38 | 39 | protected override void InternalGetBytes(ByteWriter writer) 40 | { 41 | writer.WriteRawBytes(Cookie); 42 | writer.WriteStringList(KexAlgorithms); 43 | writer.WriteStringList(ServerHostKeyAlgorithms); 44 | writer.WriteStringList(EncryptionAlgorithmsClientToServer); 45 | writer.WriteStringList(EncryptionAlgorithmsServerToClient); 46 | writer.WriteStringList(MacAlgorithmsClientToServer); 47 | writer.WriteStringList(MacAlgorithmsServerToClient); 48 | writer.WriteStringList(CompressionAlgorithmsClientToServer); 49 | writer.WriteStringList(CompressionAlgorithmsServerToClient); 50 | writer.WriteStringList(LanguagesClientToServer); 51 | writer.WriteStringList(LanguagesServerToClient); 52 | writer.WriteByte(FirstKexPacketFollows ? (byte)0x01 : (byte)0x00); 53 | writer.WriteUInt32(0); 54 | } 55 | 56 | public override void Load(ByteReader reader) 57 | { 58 | Cookie = reader.GetBytes(16); 59 | KexAlgorithms = reader.GetNameList(); 60 | ServerHostKeyAlgorithms = reader.GetNameList(); 61 | EncryptionAlgorithmsClientToServer = reader.GetNameList(); 62 | EncryptionAlgorithmsServerToClient = reader.GetNameList(); 63 | MacAlgorithmsClientToServer = reader.GetNameList(); 64 | MacAlgorithmsServerToClient = reader.GetNameList(); 65 | CompressionAlgorithmsClientToServer = reader.GetNameList(); 66 | CompressionAlgorithmsServerToClient = reader.GetNameList(); 67 | LanguagesClientToServer = reader.GetNameList(); 68 | LanguagesServerToClient = reader.GetNameList(); 69 | FirstKexPacketFollows = reader.GetBoolean(); 70 | uint reserved = reader.GetUInt32(); 71 | } 72 | 73 | public IKexAlgorithm PickKexAlgorithm() 74 | { 75 | foreach (string algo in this.KexAlgorithms) 76 | { 77 | IKexAlgorithm selectedAlgo = Server.GetType(Server.SupportedKexAlgorithms, algo); 78 | 79 | if (selectedAlgo != null) 80 | { 81 | return selectedAlgo; 82 | } 83 | 84 | } 85 | 86 | // If no algorithm satisfying all these conditions can be found, the 87 | // connection fails, and both sides MUST disconnect. 88 | throw new KSSHServerException(DisconnectReason.SSH_DISCONNECT_KEY_EXCHANGE_FAILED, "Could not find a shared Kex Algorithm"); 89 | } 90 | 91 | 92 | public IHostKeyAlgorithm PickHostKeyAlgorithm() 93 | { 94 | foreach (string algo in this.ServerHostKeyAlgorithms) 95 | { 96 | IHostKeyAlgorithm selectedAlgo = Server.GetType(Server.SupportedHostKeyAlgorithms, algo); 97 | 98 | if (selectedAlgo != null) 99 | return selectedAlgo; 100 | } 101 | 102 | // If no algorithm satisfying all these conditions can be found, the 103 | // connection fails, and both sides MUST disconnect. 104 | throw new NotSupportedException("Could not find a shared Host Key Algorithm"); 105 | } 106 | 107 | public ICipher PickCipherClientToServer() 108 | { 109 | foreach (string algo in this.EncryptionAlgorithmsClientToServer) 110 | { 111 | ICipher selectedAlgo = Server.GetType(Server.SupportedCiphers, algo); 112 | 113 | if (selectedAlgo != null) 114 | return selectedAlgo; 115 | } 116 | 117 | // If no algorithm satisfying all these conditions can be found, the 118 | // connection fails, and both sides MUST disconnect. 119 | throw new KSSHServerException(DisconnectReason.SSH_DISCONNECT_KEY_EXCHANGE_FAILED, "Could not find a shared Client-To-Server Cipher Algorithm"); 120 | } 121 | 122 | public ICipher PickCipherServerToClient() 123 | { 124 | foreach (string algo in this.EncryptionAlgorithmsServerToClient) 125 | { 126 | ICipher selectedCipher = Server.GetType(Server.SupportedCiphers, algo); 127 | if (selectedCipher != null) 128 | { 129 | return selectedCipher; 130 | } 131 | } 132 | 133 | // If no algorithm satisfying all these conditions can be found, the 134 | // connection fails, and both sides MUST disconnect. 135 | throw new NotSupportedException("Could not find a shared Server-To-Client Cipher Algorithm"); 136 | } 137 | 138 | public IMACAlgorithm PickMACAlgorithmClientToServer() 139 | { 140 | foreach (string algo in this.MacAlgorithmsClientToServer) 141 | { 142 | IMACAlgorithm selectedAlgo = Server.GetType(Server.SupportedMACAlgorithms, algo); 143 | if (selectedAlgo != null) 144 | { 145 | return selectedAlgo; 146 | } 147 | } 148 | 149 | // If no algorithm satisfying all these conditions can be found, the 150 | // connection fails, and both sides MUST disconnect. 151 | throw new KSSHServerException(DisconnectReason.SSH_DISCONNECT_KEY_EXCHANGE_FAILED, "Could not find a shared Client-To-Server MAC Algorithm"); 152 | } 153 | 154 | public IMACAlgorithm PickMACAlgorithmServerToClient() 155 | { 156 | foreach (string algo in this.MacAlgorithmsServerToClient) 157 | { 158 | IMACAlgorithm selectedAlgo = Server.GetType(Server.SupportedMACAlgorithms, algo); 159 | if (selectedAlgo != null) 160 | { 161 | return selectedAlgo; 162 | } 163 | } 164 | 165 | // If no algorithm satisfying all these conditions can be found, the 166 | // connection fails, and both sides MUST disconnect. 167 | throw new KSSHServerException(DisconnectReason.SSH_DISCONNECT_KEY_EXCHANGE_FAILED, "Could not find a shared Server-To-Client MAC Algorithm"); 168 | } 169 | 170 | public ICompression PickCompressionAlgorithmClientToServer() 171 | { 172 | foreach (string algo in this.CompressionAlgorithmsClientToServer) 173 | { 174 | ICompression selectedAlgo = Server.GetType(Server.SupportedCompressions, algo); 175 | if (selectedAlgo != null) 176 | { 177 | return selectedAlgo; 178 | } 179 | } 180 | 181 | // If no algorithm satisfying all these conditions can be found, the 182 | // connection fails, and both sides MUST disconnect. 183 | throw new KSSHServerException(DisconnectReason.SSH_DISCONNECT_KEY_EXCHANGE_FAILED, "Could not find a shared Client-To-Server Compresion Algorithm"); 184 | } 185 | 186 | public ICompression PickCompressionAlgorithmServerToClient() 187 | { 188 | foreach (string algo in this.CompressionAlgorithmsServerToClient) 189 | { 190 | ICompression selectedAlgo = Server.GetType(Server.SupportedCompressions, algo); 191 | if (selectedAlgo != null) 192 | { 193 | return selectedAlgo; 194 | } 195 | } 196 | 197 | // If no algorithm satisfying all these conditions can be found, the 198 | // connection fails, and both sides MUST disconnect. 199 | throw new KSSHServerException(DisconnectReason.SSH_DISCONNECT_KEY_EXCHANGE_FAILED, "Could not find a shared Server-To-Client Compresion Algorithm"); 200 | } 201 | } 202 | } -------------------------------------------------------------------------------- /Packets/NewKeys.cs: -------------------------------------------------------------------------------- 1 | namespace KSSHServer.Packets 2 | { 3 | public class NewKeys : Packet 4 | { 5 | public override PacketType PacketType 6 | { 7 | get 8 | { 9 | return PacketType.SSH_MSG_NEWKEYS; 10 | } 11 | } 12 | 13 | protected override void InternalGetBytes(ByteWriter writer) 14 | { 15 | // No data, nothing to write 16 | } 17 | 18 | public override void Load(ByteReader reader) 19 | { 20 | // No data, nothing to load 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Packets/Packet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Net.Sockets; 4 | using System.Collections.Generic; 5 | using System.Reflection; 6 | using System.Security.Cryptography; 7 | 8 | namespace KSSHServer.Packets 9 | { 10 | public abstract class Packet 11 | { 12 | public const int MaxPacketSize = 35000; 13 | public static int _PacketHeaderSize = 5; 14 | public abstract PacketType PacketType { get; } 15 | public uint PacketSequence { get; set; } 16 | public static readonly Dictionary _PacketTypes = new Dictionary(); 17 | 18 | static Packet() 19 | { 20 | var packets = Assembly.GetEntryAssembly().GetTypes().Where(t => typeof(Packet).IsAssignableFrom(t)); 21 | foreach (var packet in packets) 22 | { 23 | try 24 | { 25 | Packet packetInstance = Activator.CreateInstance(packet) as Packet; 26 | _PacketTypes[packetInstance.PacketType] = packet; 27 | } 28 | catch (Exception e) 29 | { 30 | string error = e.Message; 31 | } 32 | } 33 | } 34 | 35 | public byte[] ToByteArray() 36 | { 37 | // TODO: Keep track of the received packet sequence (used for MAC) 38 | 39 | byte[] payload = GetBytes(); 40 | 41 | // TODO: Compress the payload if necessary 42 | 43 | // TODO: Get the block size based on the ClientToServer cipher 44 | 45 | uint blockSize = 8; 46 | 47 | byte paddingLength = (byte)(blockSize - (payload.Length + 5) % blockSize); 48 | if (paddingLength < 4) 49 | paddingLength += (byte)blockSize; 50 | 51 | byte[] padding = new byte[paddingLength]; 52 | RandomNumberGenerator.Create().GetBytes(padding); 53 | 54 | uint packetLength = (uint)(payload.Length + paddingLength + 1); 55 | 56 | using (ByteWriter writer = new ByteWriter()) 57 | { 58 | writer.WriteUInt32(packetLength); 59 | writer.WriteByte(paddingLength); 60 | writer.WriteRawBytes(payload); 61 | writer.WriteRawBytes(padding); 62 | 63 | payload = writer.ToByteArray(); 64 | } 65 | 66 | // TODO: Encrypt the payload if necessary 67 | 68 | // TODO: Write MAC if necesssary 69 | 70 | return payload; 71 | } 72 | 73 | public byte[] GetBytes() 74 | { 75 | using (ByteWriter writer = new ByteWriter()) 76 | { 77 | writer.WritePacketType(PacketType); 78 | InternalGetBytes(writer); 79 | return writer.ToByteArray(); 80 | } 81 | } 82 | 83 | public abstract void Load(ByteReader reader); 84 | protected abstract void InternalGetBytes(ByteWriter writer); 85 | } 86 | } -------------------------------------------------------------------------------- /Packets/PacketType.cs: -------------------------------------------------------------------------------- 1 | namespace KSSHServer.Packets 2 | { 3 | public enum PacketType : byte 4 | { 5 | SSH_MSG_DISCONNECT = 1, 6 | SSH_MSG_IGNORE = 2, 7 | SSH_MSG_UNIMPLEMENTED = 3, 8 | SSH_MSG_DEBUG = 4, 9 | SSH_MSG_SERVICE_REQUEST = 5, 10 | SSH_MSG_SERVICE_ACCEPT = 6, 11 | SSH_MSG_KEXINIT = 20, 12 | SSH_MSG_NEWKEYS = 21, 13 | SSH_MSG_KEXDH_INIT = 30, 14 | SSH_MSG_KEXDH_REPLY = 31, 15 | SSH_MSG_USERAUTH_REQUEST = 50, 16 | SSH_MSG_USERAUTH_FAILURE = 51, 17 | SSH_MSG_USERAUTH_SUCCESS = 52, 18 | SSH_MSG_USERAUTH_BANNER = 53, 19 | SSH_MSG_GLOBAL_REQUEST = 80, 20 | SSH_MSG_REQUEST_SUCCESS = 81, 21 | SSH_MSG_REQUEST_FAILURE = 82, 22 | SSH_MSG_CHANNEL_OPEN = 90, 23 | SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91, 24 | SSH_MSG_CHANNEL_OPEN_FAILURE = 92, 25 | SSH_MSG_CHANNEL_WINDOW_ADJUST = 93, 26 | SSH_MSG_CHANNEL_DATA = 94, 27 | SSH_MSG_CHANNEL_EXTENDED_DATA = 95, 28 | SSH_MSG_CHANNEL_EOF = 96, 29 | SSH_MSG_CHANNEL_CLOSE = 97, 30 | SSH_MSG_CHANNEL_REQUEST = 98, 31 | SSH_MSG_CHANNEL_SUCCESS = 99, 32 | SSH_MSG_CHANNEL_FAILURE = 100 33 | } 34 | } -------------------------------------------------------------------------------- /Packets/Unimplemented.cs: -------------------------------------------------------------------------------- 1 | namespace KSSHServer.Packets 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace SSHServer.Packets 9 | { 10 | public class Unimplemented : Packet 11 | { 12 | public override PacketType PacketType 13 | { 14 | get 15 | { 16 | return PacketType.SSH_MSG_UNIMPLEMENTED; 17 | } 18 | } 19 | 20 | public uint RejectedPacketNumber { get; set; } 21 | 22 | public override void Load(ByteReader reader) 23 | { 24 | RejectedPacketNumber = reader.GetUInt32(); 25 | } 26 | 27 | protected override void InternalGetBytes(ByteWriter writer) 28 | { 29 | // uint32 packet sequence number of rejected message 30 | writer.WriteUInt32(RejectedPacketNumber); 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace KSSHServer 4 | { 5 | class Program 6 | { 7 | private static bool s_IsRunning = true; 8 | static void Main(string[] args) 9 | { 10 | Console.CancelKeyPress += Console_CancelKeyPress; 11 | 12 | Server server = new Server(); 13 | server.Start(); 14 | 15 | while (s_IsRunning) 16 | { 17 | server.Poll(); 18 | System.Threading.Thread.Sleep(25); 19 | } 20 | 21 | server.Stop(); 22 | } 23 | 24 | private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) 25 | { 26 | e.Cancel = true; 27 | s_IsRunning = false; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ksshserver 2 | Implementation of a SSH server in C# 3 | This should cover basic functionality and encryption but not the advanced features of a full blown SSH server. The idea is to cover atleast one algorithm of each type from the list of required algorithms for a client. The client in this case being Putty supporting SSH-2.0. Based on the idea by TyrenDe. 4 | 5 | Algorithms (to be) supported by this server - 6 | key exchange - diffie-hellman-group14-sha1 7 | host key algorithm - ssh-rsa 8 | encryption - 3des-cbc 9 | mac algorithm - hmac-sha1 10 | compression - none 11 | 12 | ----------------------------------------------------------- 13 | Packet Protocol (https://tools.ietf.org/html/rfc4253) 14 | 15 | uint32 packet_length 16 | byte padding_length 17 | byte[n1] payload; n1 = packet_length - padding_length - 1 18 | byte[n2] random padding; n2 = padding_length 19 | byte[m] mac (Message Authentication Code - MAC); m = mac_length 20 | 21 | ----------------------------------------------------------- 22 | 23 | Changelog 24 | 25 | 0.1 26 | - Added ability to listen for incoming connections and manage connections 27 | 28 | 0.2 29 | - Added multiple client management 30 | 31 | 0.3 32 | - Read protocol exchange string from client 33 | 34 | 0.4 35 | - Read key exchange packet (*broken) 36 | 37 | 0.5 38 | - Read key exchange packet working 39 | 40 | 0.6 41 | - Added support for cipher, mac, keyhost and encryption algorithms 42 | -------------------------------------------------------------------------------- /Server.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | using System.IO; 5 | using System.Net; 6 | using System.Net.Sockets; 7 | using System.Threading.Tasks; 8 | using System.Collections.Generic; 9 | using KSSHServer.KexAlgorithms; 10 | using KSSHServer.HostKeyAlgorithms; 11 | using KSSHServer.Packets; 12 | 13 | namespace KSSHServer 14 | { 15 | public static class ServerConstants 16 | { 17 | public const string ProtocolVersionExchange = "SSH-2.0-ksshserver"; 18 | } 19 | public class Server 20 | { 21 | private IConfigurationRoot _Configuration; 22 | private LoggerFactory _LoggerFactory; 23 | private ILogger _Logger; 24 | private const int DefaultPort = 22; 25 | private const int ConectionBacklog = 64; 26 | private TcpListener _Listener; 27 | private List _Clients = new List(); 28 | private static Dictionary _HostKeys = new Dictionary(); 29 | 30 | public Server() 31 | { 32 | _Configuration = new ConfigurationBuilder() 33 | .SetBasePath(Directory.GetCurrentDirectory()) 34 | .AddJsonFile("sshserver.json", optional: false) 35 | .Build(); 36 | 37 | _LoggerFactory = new LoggerFactory(); 38 | _LoggerFactory.AddConsole(_Configuration.GetSection("Logging")); 39 | _Logger = _LoggerFactory.CreateLogger("KSSHServer"); 40 | 41 | IConfigurationSection keys = _Configuration.GetSection("keys"); 42 | foreach (IConfigurationSection key in keys.GetChildren()) 43 | { 44 | _HostKeys[key.Key] = key.Value; 45 | } 46 | } 47 | 48 | public static IReadOnlyList SupportedHostKeyAlgorithms { get; private set; } = new List() 49 | { 50 | typeof(SSHRSA) 51 | }; 52 | 53 | public static T GetType(IReadOnlyList types, string selected) where T : class 54 | { 55 | foreach (Type type in types) 56 | { 57 | IAlgorithm algo = Activator.CreateInstance(type) as IAlgorithm; 58 | if (algo.Name.Equals(selected, StringComparison.OrdinalIgnoreCase)) 59 | { 60 | if (algo is IHostKeyAlgorithm) 61 | { 62 | ((IHostKeyAlgorithm)algo).ImportKey(_HostKeys[algo.Name]); 63 | } 64 | 65 | return algo as T; 66 | } 67 | } 68 | 69 | return default(T); 70 | } 71 | 72 | public void Start() 73 | { 74 | Stop(); 75 | 76 | _Logger.LogInformation("Starting up..."); 77 | 78 | int port = _Configuration.GetValue("port", DefaultPort); 79 | 80 | _Listener = new TcpListener(IPAddress.Any, port); 81 | _Listener.Start(ConectionBacklog); 82 | 83 | _Logger.LogInformation($"Listening on port: {port}"); 84 | } 85 | 86 | public void Stop() 87 | { 88 | if (_Listener != null) 89 | { 90 | _Logger.LogInformation("Shutting down..."); 91 | 92 | _Listener.Stop(); 93 | _Listener = null; 94 | 95 | // Disconnect each client and clear list 96 | _Clients.ForEach(c => c.Disconnect(DisconnectReason.SSH_DISCONNECT_BY_APPLICATION, "The server is getting shutdown.")); 97 | _Clients.Clear(); 98 | 99 | _Logger.LogInformation("Shutting down..."); 100 | } 101 | } 102 | 103 | public void Poll() 104 | { 105 | // Check for new connections 106 | while (_Listener.Pending()) 107 | { 108 | Task acceptTask = _Listener.AcceptSocketAsync(); 109 | acceptTask.Wait(); 110 | 111 | Socket socket = acceptTask.Result; 112 | _Logger.LogDebug($"New Client: {socket.RemoteEndPoint}"); 113 | 114 | _Clients.Add(new Client(socket, _LoggerFactory.CreateLogger(socket.RemoteEndPoint.ToString()))); 115 | 116 | } 117 | 118 | // Poll each client 119 | _Clients.ForEach(c => c.Poll()); 120 | 121 | // Remove all disconnected clients 122 | _Clients.RemoveAll(c => c.IsConnected() == false); 123 | } 124 | 125 | public static IReadOnlyList SupportedCompressions { get; private set; } = new List() 126 | { 127 | typeof(Compressions.NoCompression) 128 | }; 129 | public static IReadOnlyList SupportedMACAlgorithms { get; private set; } = new List() 130 | { 131 | typeof(MACAlgorithms.HMACSHA1) 132 | }; 133 | public static IReadOnlyList SupportedCiphers { get; private set; } = new List() 134 | { 135 | // typeof(Ciphers.NoCipher), 136 | typeof(Ciphers.TripleDESCBC) 137 | }; 138 | 139 | public static IReadOnlyList SupportedKexAlgorithms { get; private set; } = new List() 140 | { 141 | typeof(DiffieHellmanGroup14SHA1) 142 | }; 143 | 144 | public static IEnumerable GetNames(IReadOnlyList types) 145 | { 146 | foreach (Type type in types) 147 | { 148 | IAlgorithm algo = Activator.CreateInstance(type) as IAlgorithm; 149 | yield return type.Name; 150 | } 151 | } 152 | } 153 | } -------------------------------------------------------------------------------- /obj/project.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "targets": { 4 | ".NETCoreApp,Version=v2.0": { 5 | "Microsoft.NETCore.App/2.0.0": { 6 | "type": "package", 7 | "dependencies": { 8 | "Microsoft.NETCore.DotNetHostPolicy": "2.0.0", 9 | "Microsoft.NETCore.Platforms": "2.0.0", 10 | "NETStandard.Library": "2.0.0" 11 | }, 12 | "compile": { 13 | "ref/netcoreapp2.0/Microsoft.CSharp.dll": {}, 14 | "ref/netcoreapp2.0/Microsoft.VisualBasic.dll": {}, 15 | "ref/netcoreapp2.0/Microsoft.Win32.Primitives.dll": {}, 16 | "ref/netcoreapp2.0/System.AppContext.dll": {}, 17 | "ref/netcoreapp2.0/System.Buffers.dll": {}, 18 | "ref/netcoreapp2.0/System.Collections.Concurrent.dll": {}, 19 | "ref/netcoreapp2.0/System.Collections.Immutable.dll": {}, 20 | "ref/netcoreapp2.0/System.Collections.NonGeneric.dll": {}, 21 | "ref/netcoreapp2.0/System.Collections.Specialized.dll": {}, 22 | "ref/netcoreapp2.0/System.Collections.dll": {}, 23 | "ref/netcoreapp2.0/System.ComponentModel.Annotations.dll": {}, 24 | "ref/netcoreapp2.0/System.ComponentModel.Composition.dll": {}, 25 | "ref/netcoreapp2.0/System.ComponentModel.DataAnnotations.dll": {}, 26 | "ref/netcoreapp2.0/System.ComponentModel.EventBasedAsync.dll": {}, 27 | "ref/netcoreapp2.0/System.ComponentModel.Primitives.dll": {}, 28 | "ref/netcoreapp2.0/System.ComponentModel.TypeConverter.dll": {}, 29 | "ref/netcoreapp2.0/System.ComponentModel.dll": {}, 30 | "ref/netcoreapp2.0/System.Configuration.dll": {}, 31 | "ref/netcoreapp2.0/System.Console.dll": {}, 32 | "ref/netcoreapp2.0/System.Core.dll": {}, 33 | "ref/netcoreapp2.0/System.Data.Common.dll": {}, 34 | "ref/netcoreapp2.0/System.Data.dll": {}, 35 | "ref/netcoreapp2.0/System.Diagnostics.Contracts.dll": {}, 36 | "ref/netcoreapp2.0/System.Diagnostics.Debug.dll": {}, 37 | "ref/netcoreapp2.0/System.Diagnostics.DiagnosticSource.dll": {}, 38 | "ref/netcoreapp2.0/System.Diagnostics.FileVersionInfo.dll": {}, 39 | "ref/netcoreapp2.0/System.Diagnostics.Process.dll": {}, 40 | "ref/netcoreapp2.0/System.Diagnostics.StackTrace.dll": {}, 41 | "ref/netcoreapp2.0/System.Diagnostics.TextWriterTraceListener.dll": {}, 42 | "ref/netcoreapp2.0/System.Diagnostics.Tools.dll": {}, 43 | "ref/netcoreapp2.0/System.Diagnostics.TraceSource.dll": {}, 44 | "ref/netcoreapp2.0/System.Diagnostics.Tracing.dll": {}, 45 | "ref/netcoreapp2.0/System.Drawing.Primitives.dll": {}, 46 | "ref/netcoreapp2.0/System.Drawing.dll": {}, 47 | "ref/netcoreapp2.0/System.Dynamic.Runtime.dll": {}, 48 | "ref/netcoreapp2.0/System.Globalization.Calendars.dll": {}, 49 | "ref/netcoreapp2.0/System.Globalization.Extensions.dll": {}, 50 | "ref/netcoreapp2.0/System.Globalization.dll": {}, 51 | "ref/netcoreapp2.0/System.IO.Compression.FileSystem.dll": {}, 52 | "ref/netcoreapp2.0/System.IO.Compression.ZipFile.dll": {}, 53 | "ref/netcoreapp2.0/System.IO.Compression.dll": {}, 54 | "ref/netcoreapp2.0/System.IO.FileSystem.DriveInfo.dll": {}, 55 | "ref/netcoreapp2.0/System.IO.FileSystem.Primitives.dll": {}, 56 | "ref/netcoreapp2.0/System.IO.FileSystem.Watcher.dll": {}, 57 | "ref/netcoreapp2.0/System.IO.FileSystem.dll": {}, 58 | "ref/netcoreapp2.0/System.IO.IsolatedStorage.dll": {}, 59 | "ref/netcoreapp2.0/System.IO.MemoryMappedFiles.dll": {}, 60 | "ref/netcoreapp2.0/System.IO.Pipes.dll": {}, 61 | "ref/netcoreapp2.0/System.IO.UnmanagedMemoryStream.dll": {}, 62 | "ref/netcoreapp2.0/System.IO.dll": {}, 63 | "ref/netcoreapp2.0/System.Linq.Expressions.dll": {}, 64 | "ref/netcoreapp2.0/System.Linq.Parallel.dll": {}, 65 | "ref/netcoreapp2.0/System.Linq.Queryable.dll": {}, 66 | "ref/netcoreapp2.0/System.Linq.dll": {}, 67 | "ref/netcoreapp2.0/System.Net.Http.dll": {}, 68 | "ref/netcoreapp2.0/System.Net.HttpListener.dll": {}, 69 | "ref/netcoreapp2.0/System.Net.Mail.dll": {}, 70 | "ref/netcoreapp2.0/System.Net.NameResolution.dll": {}, 71 | "ref/netcoreapp2.0/System.Net.NetworkInformation.dll": {}, 72 | "ref/netcoreapp2.0/System.Net.Ping.dll": {}, 73 | "ref/netcoreapp2.0/System.Net.Primitives.dll": {}, 74 | "ref/netcoreapp2.0/System.Net.Requests.dll": {}, 75 | "ref/netcoreapp2.0/System.Net.Security.dll": {}, 76 | "ref/netcoreapp2.0/System.Net.ServicePoint.dll": {}, 77 | "ref/netcoreapp2.0/System.Net.Sockets.dll": {}, 78 | "ref/netcoreapp2.0/System.Net.WebClient.dll": {}, 79 | "ref/netcoreapp2.0/System.Net.WebHeaderCollection.dll": {}, 80 | "ref/netcoreapp2.0/System.Net.WebProxy.dll": {}, 81 | "ref/netcoreapp2.0/System.Net.WebSockets.Client.dll": {}, 82 | "ref/netcoreapp2.0/System.Net.WebSockets.dll": {}, 83 | "ref/netcoreapp2.0/System.Net.dll": {}, 84 | "ref/netcoreapp2.0/System.Numerics.Vectors.dll": {}, 85 | "ref/netcoreapp2.0/System.Numerics.dll": {}, 86 | "ref/netcoreapp2.0/System.ObjectModel.dll": {}, 87 | "ref/netcoreapp2.0/System.Reflection.DispatchProxy.dll": {}, 88 | "ref/netcoreapp2.0/System.Reflection.Emit.ILGeneration.dll": {}, 89 | "ref/netcoreapp2.0/System.Reflection.Emit.Lightweight.dll": {}, 90 | "ref/netcoreapp2.0/System.Reflection.Emit.dll": {}, 91 | "ref/netcoreapp2.0/System.Reflection.Extensions.dll": {}, 92 | "ref/netcoreapp2.0/System.Reflection.Metadata.dll": {}, 93 | "ref/netcoreapp2.0/System.Reflection.Primitives.dll": {}, 94 | "ref/netcoreapp2.0/System.Reflection.TypeExtensions.dll": {}, 95 | "ref/netcoreapp2.0/System.Reflection.dll": {}, 96 | "ref/netcoreapp2.0/System.Resources.Reader.dll": {}, 97 | "ref/netcoreapp2.0/System.Resources.ResourceManager.dll": {}, 98 | "ref/netcoreapp2.0/System.Resources.Writer.dll": {}, 99 | "ref/netcoreapp2.0/System.Runtime.CompilerServices.VisualC.dll": {}, 100 | "ref/netcoreapp2.0/System.Runtime.Extensions.dll": {}, 101 | "ref/netcoreapp2.0/System.Runtime.Handles.dll": {}, 102 | "ref/netcoreapp2.0/System.Runtime.InteropServices.RuntimeInformation.dll": {}, 103 | "ref/netcoreapp2.0/System.Runtime.InteropServices.WindowsRuntime.dll": {}, 104 | "ref/netcoreapp2.0/System.Runtime.InteropServices.dll": {}, 105 | "ref/netcoreapp2.0/System.Runtime.Loader.dll": {}, 106 | "ref/netcoreapp2.0/System.Runtime.Numerics.dll": {}, 107 | "ref/netcoreapp2.0/System.Runtime.Serialization.Formatters.dll": {}, 108 | "ref/netcoreapp2.0/System.Runtime.Serialization.Json.dll": {}, 109 | "ref/netcoreapp2.0/System.Runtime.Serialization.Primitives.dll": {}, 110 | "ref/netcoreapp2.0/System.Runtime.Serialization.Xml.dll": {}, 111 | "ref/netcoreapp2.0/System.Runtime.Serialization.dll": {}, 112 | "ref/netcoreapp2.0/System.Runtime.dll": {}, 113 | "ref/netcoreapp2.0/System.Security.Claims.dll": {}, 114 | "ref/netcoreapp2.0/System.Security.Cryptography.Algorithms.dll": {}, 115 | "ref/netcoreapp2.0/System.Security.Cryptography.Csp.dll": {}, 116 | "ref/netcoreapp2.0/System.Security.Cryptography.Encoding.dll": {}, 117 | "ref/netcoreapp2.0/System.Security.Cryptography.Primitives.dll": {}, 118 | "ref/netcoreapp2.0/System.Security.Cryptography.X509Certificates.dll": {}, 119 | "ref/netcoreapp2.0/System.Security.Principal.dll": {}, 120 | "ref/netcoreapp2.0/System.Security.SecureString.dll": {}, 121 | "ref/netcoreapp2.0/System.Security.dll": {}, 122 | "ref/netcoreapp2.0/System.ServiceModel.Web.dll": {}, 123 | "ref/netcoreapp2.0/System.ServiceProcess.dll": {}, 124 | "ref/netcoreapp2.0/System.Text.Encoding.Extensions.dll": {}, 125 | "ref/netcoreapp2.0/System.Text.Encoding.dll": {}, 126 | "ref/netcoreapp2.0/System.Text.RegularExpressions.dll": {}, 127 | "ref/netcoreapp2.0/System.Threading.Overlapped.dll": {}, 128 | "ref/netcoreapp2.0/System.Threading.Tasks.Dataflow.dll": {}, 129 | "ref/netcoreapp2.0/System.Threading.Tasks.Extensions.dll": {}, 130 | "ref/netcoreapp2.0/System.Threading.Tasks.Parallel.dll": {}, 131 | "ref/netcoreapp2.0/System.Threading.Tasks.dll": {}, 132 | "ref/netcoreapp2.0/System.Threading.Thread.dll": {}, 133 | "ref/netcoreapp2.0/System.Threading.ThreadPool.dll": {}, 134 | "ref/netcoreapp2.0/System.Threading.Timer.dll": {}, 135 | "ref/netcoreapp2.0/System.Threading.dll": {}, 136 | "ref/netcoreapp2.0/System.Transactions.Local.dll": {}, 137 | "ref/netcoreapp2.0/System.Transactions.dll": {}, 138 | "ref/netcoreapp2.0/System.ValueTuple.dll": {}, 139 | "ref/netcoreapp2.0/System.Web.HttpUtility.dll": {}, 140 | "ref/netcoreapp2.0/System.Web.dll": {}, 141 | "ref/netcoreapp2.0/System.Windows.dll": {}, 142 | "ref/netcoreapp2.0/System.Xml.Linq.dll": {}, 143 | "ref/netcoreapp2.0/System.Xml.ReaderWriter.dll": {}, 144 | "ref/netcoreapp2.0/System.Xml.Serialization.dll": {}, 145 | "ref/netcoreapp2.0/System.Xml.XDocument.dll": {}, 146 | "ref/netcoreapp2.0/System.Xml.XPath.XDocument.dll": {}, 147 | "ref/netcoreapp2.0/System.Xml.XPath.dll": {}, 148 | "ref/netcoreapp2.0/System.Xml.XmlDocument.dll": {}, 149 | "ref/netcoreapp2.0/System.Xml.XmlSerializer.dll": {}, 150 | "ref/netcoreapp2.0/System.Xml.dll": {}, 151 | "ref/netcoreapp2.0/System.dll": {}, 152 | "ref/netcoreapp2.0/WindowsBase.dll": {}, 153 | "ref/netcoreapp2.0/mscorlib.dll": {}, 154 | "ref/netcoreapp2.0/netstandard.dll": {} 155 | }, 156 | "build": { 157 | "build/netcoreapp2.0/Microsoft.NETCore.App.props": {}, 158 | "build/netcoreapp2.0/Microsoft.NETCore.App.targets": {} 159 | } 160 | }, 161 | "Microsoft.NETCore.DotNetAppHost/2.0.0": { 162 | "type": "package" 163 | }, 164 | "Microsoft.NETCore.DotNetHostPolicy/2.0.0": { 165 | "type": "package", 166 | "dependencies": { 167 | "Microsoft.NETCore.DotNetHostResolver": "2.0.0" 168 | } 169 | }, 170 | "Microsoft.NETCore.DotNetHostResolver/2.0.0": { 171 | "type": "package", 172 | "dependencies": { 173 | "Microsoft.NETCore.DotNetAppHost": "2.0.0" 174 | } 175 | }, 176 | "Microsoft.NETCore.Platforms/2.0.0": { 177 | "type": "package", 178 | "compile": { 179 | "lib/netstandard1.0/_._": {} 180 | }, 181 | "runtime": { 182 | "lib/netstandard1.0/_._": {} 183 | } 184 | }, 185 | "NETStandard.Library/2.0.0": { 186 | "type": "package", 187 | "dependencies": { 188 | "Microsoft.NETCore.Platforms": "1.1.0" 189 | }, 190 | "compile": { 191 | "lib/netstandard1.0/_._": {} 192 | }, 193 | "runtime": { 194 | "lib/netstandard1.0/_._": {} 195 | }, 196 | "build": { 197 | "build/netstandard2.0/NETStandard.Library.targets": {} 198 | } 199 | } 200 | } 201 | }, 202 | "libraries": { 203 | "Microsoft.NETCore.App/2.0.0": { 204 | "sha512": "/mzXF+UtZef+VpzzN88EpvFq5U6z4rj54ZMq/J968H6pcvyLOmcupmTRpJ3CJm8ILoCGh9WI7qpDdiKtuzswrQ==", 205 | "type": "package", 206 | "path": "microsoft.netcore.app/2.0.0", 207 | "files": [ 208 | "LICENSE.TXT", 209 | "Microsoft.NETCore.App.versions.txt", 210 | "THIRD-PARTY-NOTICES.TXT", 211 | "build/netcoreapp2.0/Microsoft.NETCore.App.PlatformManifest.txt", 212 | "build/netcoreapp2.0/Microsoft.NETCore.App.props", 213 | "build/netcoreapp2.0/Microsoft.NETCore.App.targets", 214 | "microsoft.netcore.app.2.0.0.nupkg.sha512", 215 | "microsoft.netcore.app.nuspec", 216 | "ref/netcoreapp/_._", 217 | "ref/netcoreapp2.0/Microsoft.CSharp.dll", 218 | "ref/netcoreapp2.0/Microsoft.CSharp.xml", 219 | "ref/netcoreapp2.0/Microsoft.VisualBasic.dll", 220 | "ref/netcoreapp2.0/Microsoft.VisualBasic.xml", 221 | "ref/netcoreapp2.0/Microsoft.Win32.Primitives.dll", 222 | "ref/netcoreapp2.0/Microsoft.Win32.Primitives.xml", 223 | "ref/netcoreapp2.0/System.AppContext.dll", 224 | "ref/netcoreapp2.0/System.AppContext.xml", 225 | "ref/netcoreapp2.0/System.Buffers.dll", 226 | "ref/netcoreapp2.0/System.Buffers.xml", 227 | "ref/netcoreapp2.0/System.Collections.Concurrent.dll", 228 | "ref/netcoreapp2.0/System.Collections.Concurrent.xml", 229 | "ref/netcoreapp2.0/System.Collections.Immutable.dll", 230 | "ref/netcoreapp2.0/System.Collections.Immutable.xml", 231 | "ref/netcoreapp2.0/System.Collections.NonGeneric.dll", 232 | "ref/netcoreapp2.0/System.Collections.NonGeneric.xml", 233 | "ref/netcoreapp2.0/System.Collections.Specialized.dll", 234 | "ref/netcoreapp2.0/System.Collections.Specialized.xml", 235 | "ref/netcoreapp2.0/System.Collections.dll", 236 | "ref/netcoreapp2.0/System.Collections.xml", 237 | "ref/netcoreapp2.0/System.ComponentModel.Annotations.dll", 238 | "ref/netcoreapp2.0/System.ComponentModel.Annotations.xml", 239 | "ref/netcoreapp2.0/System.ComponentModel.Composition.dll", 240 | "ref/netcoreapp2.0/System.ComponentModel.DataAnnotations.dll", 241 | "ref/netcoreapp2.0/System.ComponentModel.EventBasedAsync.dll", 242 | "ref/netcoreapp2.0/System.ComponentModel.EventBasedAsync.xml", 243 | "ref/netcoreapp2.0/System.ComponentModel.Primitives.dll", 244 | "ref/netcoreapp2.0/System.ComponentModel.Primitives.xml", 245 | "ref/netcoreapp2.0/System.ComponentModel.TypeConverter.dll", 246 | "ref/netcoreapp2.0/System.ComponentModel.TypeConverter.xml", 247 | "ref/netcoreapp2.0/System.ComponentModel.dll", 248 | "ref/netcoreapp2.0/System.ComponentModel.xml", 249 | "ref/netcoreapp2.0/System.Configuration.dll", 250 | "ref/netcoreapp2.0/System.Console.dll", 251 | "ref/netcoreapp2.0/System.Console.xml", 252 | "ref/netcoreapp2.0/System.Core.dll", 253 | "ref/netcoreapp2.0/System.Data.Common.dll", 254 | "ref/netcoreapp2.0/System.Data.Common.xml", 255 | "ref/netcoreapp2.0/System.Data.dll", 256 | "ref/netcoreapp2.0/System.Diagnostics.Contracts.dll", 257 | "ref/netcoreapp2.0/System.Diagnostics.Contracts.xml", 258 | "ref/netcoreapp2.0/System.Diagnostics.Debug.dll", 259 | "ref/netcoreapp2.0/System.Diagnostics.Debug.xml", 260 | "ref/netcoreapp2.0/System.Diagnostics.DiagnosticSource.dll", 261 | "ref/netcoreapp2.0/System.Diagnostics.DiagnosticSource.xml", 262 | "ref/netcoreapp2.0/System.Diagnostics.FileVersionInfo.dll", 263 | "ref/netcoreapp2.0/System.Diagnostics.FileVersionInfo.xml", 264 | "ref/netcoreapp2.0/System.Diagnostics.Process.dll", 265 | "ref/netcoreapp2.0/System.Diagnostics.Process.xml", 266 | "ref/netcoreapp2.0/System.Diagnostics.StackTrace.dll", 267 | "ref/netcoreapp2.0/System.Diagnostics.StackTrace.xml", 268 | "ref/netcoreapp2.0/System.Diagnostics.TextWriterTraceListener.dll", 269 | "ref/netcoreapp2.0/System.Diagnostics.TextWriterTraceListener.xml", 270 | "ref/netcoreapp2.0/System.Diagnostics.Tools.dll", 271 | "ref/netcoreapp2.0/System.Diagnostics.Tools.xml", 272 | "ref/netcoreapp2.0/System.Diagnostics.TraceSource.dll", 273 | "ref/netcoreapp2.0/System.Diagnostics.TraceSource.xml", 274 | "ref/netcoreapp2.0/System.Diagnostics.Tracing.dll", 275 | "ref/netcoreapp2.0/System.Diagnostics.Tracing.xml", 276 | "ref/netcoreapp2.0/System.Drawing.Primitives.dll", 277 | "ref/netcoreapp2.0/System.Drawing.Primitives.xml", 278 | "ref/netcoreapp2.0/System.Drawing.dll", 279 | "ref/netcoreapp2.0/System.Dynamic.Runtime.dll", 280 | "ref/netcoreapp2.0/System.Dynamic.Runtime.xml", 281 | "ref/netcoreapp2.0/System.Globalization.Calendars.dll", 282 | "ref/netcoreapp2.0/System.Globalization.Calendars.xml", 283 | "ref/netcoreapp2.0/System.Globalization.Extensions.dll", 284 | "ref/netcoreapp2.0/System.Globalization.Extensions.xml", 285 | "ref/netcoreapp2.0/System.Globalization.dll", 286 | "ref/netcoreapp2.0/System.Globalization.xml", 287 | "ref/netcoreapp2.0/System.IO.Compression.FileSystem.dll", 288 | "ref/netcoreapp2.0/System.IO.Compression.ZipFile.dll", 289 | "ref/netcoreapp2.0/System.IO.Compression.ZipFile.xml", 290 | "ref/netcoreapp2.0/System.IO.Compression.dll", 291 | "ref/netcoreapp2.0/System.IO.Compression.xml", 292 | "ref/netcoreapp2.0/System.IO.FileSystem.DriveInfo.dll", 293 | "ref/netcoreapp2.0/System.IO.FileSystem.DriveInfo.xml", 294 | "ref/netcoreapp2.0/System.IO.FileSystem.Primitives.dll", 295 | "ref/netcoreapp2.0/System.IO.FileSystem.Primitives.xml", 296 | "ref/netcoreapp2.0/System.IO.FileSystem.Watcher.dll", 297 | "ref/netcoreapp2.0/System.IO.FileSystem.Watcher.xml", 298 | "ref/netcoreapp2.0/System.IO.FileSystem.dll", 299 | "ref/netcoreapp2.0/System.IO.FileSystem.xml", 300 | "ref/netcoreapp2.0/System.IO.IsolatedStorage.dll", 301 | "ref/netcoreapp2.0/System.IO.IsolatedStorage.xml", 302 | "ref/netcoreapp2.0/System.IO.MemoryMappedFiles.dll", 303 | "ref/netcoreapp2.0/System.IO.MemoryMappedFiles.xml", 304 | "ref/netcoreapp2.0/System.IO.Pipes.dll", 305 | "ref/netcoreapp2.0/System.IO.Pipes.xml", 306 | "ref/netcoreapp2.0/System.IO.UnmanagedMemoryStream.dll", 307 | "ref/netcoreapp2.0/System.IO.UnmanagedMemoryStream.xml", 308 | "ref/netcoreapp2.0/System.IO.dll", 309 | "ref/netcoreapp2.0/System.IO.xml", 310 | "ref/netcoreapp2.0/System.Linq.Expressions.dll", 311 | "ref/netcoreapp2.0/System.Linq.Expressions.xml", 312 | "ref/netcoreapp2.0/System.Linq.Parallel.dll", 313 | "ref/netcoreapp2.0/System.Linq.Parallel.xml", 314 | "ref/netcoreapp2.0/System.Linq.Queryable.dll", 315 | "ref/netcoreapp2.0/System.Linq.Queryable.xml", 316 | "ref/netcoreapp2.0/System.Linq.dll", 317 | "ref/netcoreapp2.0/System.Linq.xml", 318 | "ref/netcoreapp2.0/System.Net.Http.dll", 319 | "ref/netcoreapp2.0/System.Net.Http.xml", 320 | "ref/netcoreapp2.0/System.Net.HttpListener.dll", 321 | "ref/netcoreapp2.0/System.Net.HttpListener.xml", 322 | "ref/netcoreapp2.0/System.Net.Mail.dll", 323 | "ref/netcoreapp2.0/System.Net.Mail.xml", 324 | "ref/netcoreapp2.0/System.Net.NameResolution.dll", 325 | "ref/netcoreapp2.0/System.Net.NameResolution.xml", 326 | "ref/netcoreapp2.0/System.Net.NetworkInformation.dll", 327 | "ref/netcoreapp2.0/System.Net.NetworkInformation.xml", 328 | "ref/netcoreapp2.0/System.Net.Ping.dll", 329 | "ref/netcoreapp2.0/System.Net.Ping.xml", 330 | "ref/netcoreapp2.0/System.Net.Primitives.dll", 331 | "ref/netcoreapp2.0/System.Net.Primitives.xml", 332 | "ref/netcoreapp2.0/System.Net.Requests.dll", 333 | "ref/netcoreapp2.0/System.Net.Requests.xml", 334 | "ref/netcoreapp2.0/System.Net.Security.dll", 335 | "ref/netcoreapp2.0/System.Net.Security.xml", 336 | "ref/netcoreapp2.0/System.Net.ServicePoint.dll", 337 | "ref/netcoreapp2.0/System.Net.ServicePoint.xml", 338 | "ref/netcoreapp2.0/System.Net.Sockets.dll", 339 | "ref/netcoreapp2.0/System.Net.Sockets.xml", 340 | "ref/netcoreapp2.0/System.Net.WebClient.dll", 341 | "ref/netcoreapp2.0/System.Net.WebClient.xml", 342 | "ref/netcoreapp2.0/System.Net.WebHeaderCollection.dll", 343 | "ref/netcoreapp2.0/System.Net.WebHeaderCollection.xml", 344 | "ref/netcoreapp2.0/System.Net.WebProxy.dll", 345 | "ref/netcoreapp2.0/System.Net.WebProxy.xml", 346 | "ref/netcoreapp2.0/System.Net.WebSockets.Client.dll", 347 | "ref/netcoreapp2.0/System.Net.WebSockets.Client.xml", 348 | "ref/netcoreapp2.0/System.Net.WebSockets.dll", 349 | "ref/netcoreapp2.0/System.Net.WebSockets.xml", 350 | "ref/netcoreapp2.0/System.Net.dll", 351 | "ref/netcoreapp2.0/System.Numerics.Vectors.dll", 352 | "ref/netcoreapp2.0/System.Numerics.Vectors.xml", 353 | "ref/netcoreapp2.0/System.Numerics.dll", 354 | "ref/netcoreapp2.0/System.ObjectModel.dll", 355 | "ref/netcoreapp2.0/System.ObjectModel.xml", 356 | "ref/netcoreapp2.0/System.Reflection.DispatchProxy.dll", 357 | "ref/netcoreapp2.0/System.Reflection.DispatchProxy.xml", 358 | "ref/netcoreapp2.0/System.Reflection.Emit.ILGeneration.dll", 359 | "ref/netcoreapp2.0/System.Reflection.Emit.ILGeneration.xml", 360 | "ref/netcoreapp2.0/System.Reflection.Emit.Lightweight.dll", 361 | "ref/netcoreapp2.0/System.Reflection.Emit.Lightweight.xml", 362 | "ref/netcoreapp2.0/System.Reflection.Emit.dll", 363 | "ref/netcoreapp2.0/System.Reflection.Emit.xml", 364 | "ref/netcoreapp2.0/System.Reflection.Extensions.dll", 365 | "ref/netcoreapp2.0/System.Reflection.Extensions.xml", 366 | "ref/netcoreapp2.0/System.Reflection.Metadata.dll", 367 | "ref/netcoreapp2.0/System.Reflection.Metadata.xml", 368 | "ref/netcoreapp2.0/System.Reflection.Primitives.dll", 369 | "ref/netcoreapp2.0/System.Reflection.Primitives.xml", 370 | "ref/netcoreapp2.0/System.Reflection.TypeExtensions.dll", 371 | "ref/netcoreapp2.0/System.Reflection.TypeExtensions.xml", 372 | "ref/netcoreapp2.0/System.Reflection.dll", 373 | "ref/netcoreapp2.0/System.Reflection.xml", 374 | "ref/netcoreapp2.0/System.Resources.Reader.dll", 375 | "ref/netcoreapp2.0/System.Resources.Reader.xml", 376 | "ref/netcoreapp2.0/System.Resources.ResourceManager.dll", 377 | "ref/netcoreapp2.0/System.Resources.ResourceManager.xml", 378 | "ref/netcoreapp2.0/System.Resources.Writer.dll", 379 | "ref/netcoreapp2.0/System.Resources.Writer.xml", 380 | "ref/netcoreapp2.0/System.Runtime.CompilerServices.VisualC.dll", 381 | "ref/netcoreapp2.0/System.Runtime.CompilerServices.VisualC.xml", 382 | "ref/netcoreapp2.0/System.Runtime.Extensions.dll", 383 | "ref/netcoreapp2.0/System.Runtime.Extensions.xml", 384 | "ref/netcoreapp2.0/System.Runtime.Handles.dll", 385 | "ref/netcoreapp2.0/System.Runtime.Handles.xml", 386 | "ref/netcoreapp2.0/System.Runtime.InteropServices.RuntimeInformation.dll", 387 | "ref/netcoreapp2.0/System.Runtime.InteropServices.RuntimeInformation.xml", 388 | "ref/netcoreapp2.0/System.Runtime.InteropServices.WindowsRuntime.dll", 389 | "ref/netcoreapp2.0/System.Runtime.InteropServices.WindowsRuntime.xml", 390 | "ref/netcoreapp2.0/System.Runtime.InteropServices.dll", 391 | "ref/netcoreapp2.0/System.Runtime.InteropServices.xml", 392 | "ref/netcoreapp2.0/System.Runtime.Loader.dll", 393 | "ref/netcoreapp2.0/System.Runtime.Loader.xml", 394 | "ref/netcoreapp2.0/System.Runtime.Numerics.dll", 395 | "ref/netcoreapp2.0/System.Runtime.Numerics.xml", 396 | "ref/netcoreapp2.0/System.Runtime.Serialization.Formatters.dll", 397 | "ref/netcoreapp2.0/System.Runtime.Serialization.Formatters.xml", 398 | "ref/netcoreapp2.0/System.Runtime.Serialization.Json.dll", 399 | "ref/netcoreapp2.0/System.Runtime.Serialization.Json.xml", 400 | "ref/netcoreapp2.0/System.Runtime.Serialization.Primitives.dll", 401 | "ref/netcoreapp2.0/System.Runtime.Serialization.Primitives.xml", 402 | "ref/netcoreapp2.0/System.Runtime.Serialization.Xml.dll", 403 | "ref/netcoreapp2.0/System.Runtime.Serialization.Xml.xml", 404 | "ref/netcoreapp2.0/System.Runtime.Serialization.dll", 405 | "ref/netcoreapp2.0/System.Runtime.dll", 406 | "ref/netcoreapp2.0/System.Runtime.xml", 407 | "ref/netcoreapp2.0/System.Security.Claims.dll", 408 | "ref/netcoreapp2.0/System.Security.Claims.xml", 409 | "ref/netcoreapp2.0/System.Security.Cryptography.Algorithms.dll", 410 | "ref/netcoreapp2.0/System.Security.Cryptography.Algorithms.xml", 411 | "ref/netcoreapp2.0/System.Security.Cryptography.Csp.dll", 412 | "ref/netcoreapp2.0/System.Security.Cryptography.Csp.xml", 413 | "ref/netcoreapp2.0/System.Security.Cryptography.Encoding.dll", 414 | "ref/netcoreapp2.0/System.Security.Cryptography.Encoding.xml", 415 | "ref/netcoreapp2.0/System.Security.Cryptography.Primitives.dll", 416 | "ref/netcoreapp2.0/System.Security.Cryptography.Primitives.xml", 417 | "ref/netcoreapp2.0/System.Security.Cryptography.X509Certificates.dll", 418 | "ref/netcoreapp2.0/System.Security.Cryptography.X509Certificates.xml", 419 | "ref/netcoreapp2.0/System.Security.Principal.dll", 420 | "ref/netcoreapp2.0/System.Security.Principal.xml", 421 | "ref/netcoreapp2.0/System.Security.SecureString.dll", 422 | "ref/netcoreapp2.0/System.Security.SecureString.xml", 423 | "ref/netcoreapp2.0/System.Security.dll", 424 | "ref/netcoreapp2.0/System.ServiceModel.Web.dll", 425 | "ref/netcoreapp2.0/System.ServiceProcess.dll", 426 | "ref/netcoreapp2.0/System.Text.Encoding.Extensions.dll", 427 | "ref/netcoreapp2.0/System.Text.Encoding.Extensions.xml", 428 | "ref/netcoreapp2.0/System.Text.Encoding.dll", 429 | "ref/netcoreapp2.0/System.Text.Encoding.xml", 430 | "ref/netcoreapp2.0/System.Text.RegularExpressions.dll", 431 | "ref/netcoreapp2.0/System.Text.RegularExpressions.xml", 432 | "ref/netcoreapp2.0/System.Threading.Overlapped.dll", 433 | "ref/netcoreapp2.0/System.Threading.Overlapped.xml", 434 | "ref/netcoreapp2.0/System.Threading.Tasks.Dataflow.dll", 435 | "ref/netcoreapp2.0/System.Threading.Tasks.Dataflow.xml", 436 | "ref/netcoreapp2.0/System.Threading.Tasks.Extensions.dll", 437 | "ref/netcoreapp2.0/System.Threading.Tasks.Extensions.xml", 438 | "ref/netcoreapp2.0/System.Threading.Tasks.Parallel.dll", 439 | "ref/netcoreapp2.0/System.Threading.Tasks.Parallel.xml", 440 | "ref/netcoreapp2.0/System.Threading.Tasks.dll", 441 | "ref/netcoreapp2.0/System.Threading.Tasks.xml", 442 | "ref/netcoreapp2.0/System.Threading.Thread.dll", 443 | "ref/netcoreapp2.0/System.Threading.Thread.xml", 444 | "ref/netcoreapp2.0/System.Threading.ThreadPool.dll", 445 | "ref/netcoreapp2.0/System.Threading.ThreadPool.xml", 446 | "ref/netcoreapp2.0/System.Threading.Timer.dll", 447 | "ref/netcoreapp2.0/System.Threading.Timer.xml", 448 | "ref/netcoreapp2.0/System.Threading.dll", 449 | "ref/netcoreapp2.0/System.Threading.xml", 450 | "ref/netcoreapp2.0/System.Transactions.Local.dll", 451 | "ref/netcoreapp2.0/System.Transactions.Local.xml", 452 | "ref/netcoreapp2.0/System.Transactions.dll", 453 | "ref/netcoreapp2.0/System.ValueTuple.dll", 454 | "ref/netcoreapp2.0/System.ValueTuple.xml", 455 | "ref/netcoreapp2.0/System.Web.HttpUtility.dll", 456 | "ref/netcoreapp2.0/System.Web.HttpUtility.xml", 457 | "ref/netcoreapp2.0/System.Web.dll", 458 | "ref/netcoreapp2.0/System.Windows.dll", 459 | "ref/netcoreapp2.0/System.Xml.Linq.dll", 460 | "ref/netcoreapp2.0/System.Xml.ReaderWriter.dll", 461 | "ref/netcoreapp2.0/System.Xml.ReaderWriter.xml", 462 | "ref/netcoreapp2.0/System.Xml.Serialization.dll", 463 | "ref/netcoreapp2.0/System.Xml.XDocument.dll", 464 | "ref/netcoreapp2.0/System.Xml.XDocument.xml", 465 | "ref/netcoreapp2.0/System.Xml.XPath.XDocument.dll", 466 | "ref/netcoreapp2.0/System.Xml.XPath.XDocument.xml", 467 | "ref/netcoreapp2.0/System.Xml.XPath.dll", 468 | "ref/netcoreapp2.0/System.Xml.XPath.xml", 469 | "ref/netcoreapp2.0/System.Xml.XmlDocument.dll", 470 | "ref/netcoreapp2.0/System.Xml.XmlDocument.xml", 471 | "ref/netcoreapp2.0/System.Xml.XmlSerializer.dll", 472 | "ref/netcoreapp2.0/System.Xml.XmlSerializer.xml", 473 | "ref/netcoreapp2.0/System.Xml.dll", 474 | "ref/netcoreapp2.0/System.dll", 475 | "ref/netcoreapp2.0/WindowsBase.dll", 476 | "ref/netcoreapp2.0/mscorlib.dll", 477 | "ref/netcoreapp2.0/netstandard.dll", 478 | "runtime.json" 479 | ] 480 | }, 481 | "Microsoft.NETCore.DotNetAppHost/2.0.0": { 482 | "sha512": "L4GGkcI/Mxl8PKLRpFdGmLb5oI8sGIR05bDTGkzCoamAjdUl1Zhkov2swjEsZvKYT8kkdiz39LtwyGYuCJxm1A==", 483 | "type": "package", 484 | "path": "microsoft.netcore.dotnetapphost/2.0.0", 485 | "files": [ 486 | "LICENSE.TXT", 487 | "THIRD-PARTY-NOTICES.TXT", 488 | "microsoft.netcore.dotnetapphost.2.0.0.nupkg.sha512", 489 | "microsoft.netcore.dotnetapphost.nuspec", 490 | "runtime.json" 491 | ] 492 | }, 493 | "Microsoft.NETCore.DotNetHostPolicy/2.0.0": { 494 | "sha512": "rm7mMn0A93fwyAwVhbyOCcPuu2hZNL0A0dAur9sNG9pEkONPfCEQeF7m2mC8KpqZO0Ol6tpV5J0AF3HTXT3GXA==", 495 | "type": "package", 496 | "path": "microsoft.netcore.dotnethostpolicy/2.0.0", 497 | "files": [ 498 | "LICENSE.TXT", 499 | "THIRD-PARTY-NOTICES.TXT", 500 | "microsoft.netcore.dotnethostpolicy.2.0.0.nupkg.sha512", 501 | "microsoft.netcore.dotnethostpolicy.nuspec", 502 | "runtime.json" 503 | ] 504 | }, 505 | "Microsoft.NETCore.DotNetHostResolver/2.0.0": { 506 | "sha512": "uBbjpeSrwsaTCADZCzRk+3aBzNnMqkC4zftJWBsL+Zk+8u+W+/lMb2thM5Y4hiVrv1YQg9t6dKldXzOKkY+pQw==", 507 | "type": "package", 508 | "path": "microsoft.netcore.dotnethostresolver/2.0.0", 509 | "files": [ 510 | "LICENSE.TXT", 511 | "THIRD-PARTY-NOTICES.TXT", 512 | "microsoft.netcore.dotnethostresolver.2.0.0.nupkg.sha512", 513 | "microsoft.netcore.dotnethostresolver.nuspec", 514 | "runtime.json" 515 | ] 516 | }, 517 | "Microsoft.NETCore.Platforms/2.0.0": { 518 | "sha512": "VdLJOCXhZaEMY7Hm2GKiULmn7IEPFE4XC5LPSfBVCUIA8YLZVh846gtfBJalsPQF2PlzdD7ecX7DZEulJ402ZQ==", 519 | "type": "package", 520 | "path": "microsoft.netcore.platforms/2.0.0", 521 | "files": [ 522 | "LICENSE.TXT", 523 | "THIRD-PARTY-NOTICES.TXT", 524 | "lib/netstandard1.0/_._", 525 | "microsoft.netcore.platforms.2.0.0.nupkg.sha512", 526 | "microsoft.netcore.platforms.nuspec", 527 | "runtime.json", 528 | "useSharedDesignerContext.txt", 529 | "version.txt" 530 | ] 531 | }, 532 | "NETStandard.Library/2.0.0": { 533 | "sha512": "7jnbRU+L08FXKMxqUflxEXtVymWvNOrS8yHgu9s6EM8Anr6T/wIX4nZ08j/u3Asz+tCufp3YVwFSEvFTPYmBPA==", 534 | "type": "package", 535 | "path": "netstandard.library/2.0.0", 536 | "files": [ 537 | "LICENSE.TXT", 538 | "THIRD-PARTY-NOTICES.TXT", 539 | "build/NETStandard.Library.targets", 540 | "build/netstandard2.0/NETStandard.Library.targets", 541 | "build/netstandard2.0/ref/Microsoft.Win32.Primitives.dll", 542 | "build/netstandard2.0/ref/System.AppContext.dll", 543 | "build/netstandard2.0/ref/System.Collections.Concurrent.dll", 544 | "build/netstandard2.0/ref/System.Collections.NonGeneric.dll", 545 | "build/netstandard2.0/ref/System.Collections.Specialized.dll", 546 | "build/netstandard2.0/ref/System.Collections.dll", 547 | "build/netstandard2.0/ref/System.ComponentModel.Composition.dll", 548 | "build/netstandard2.0/ref/System.ComponentModel.EventBasedAsync.dll", 549 | "build/netstandard2.0/ref/System.ComponentModel.Primitives.dll", 550 | "build/netstandard2.0/ref/System.ComponentModel.TypeConverter.dll", 551 | "build/netstandard2.0/ref/System.ComponentModel.dll", 552 | "build/netstandard2.0/ref/System.Console.dll", 553 | "build/netstandard2.0/ref/System.Core.dll", 554 | "build/netstandard2.0/ref/System.Data.Common.dll", 555 | "build/netstandard2.0/ref/System.Data.dll", 556 | "build/netstandard2.0/ref/System.Diagnostics.Contracts.dll", 557 | "build/netstandard2.0/ref/System.Diagnostics.Debug.dll", 558 | "build/netstandard2.0/ref/System.Diagnostics.FileVersionInfo.dll", 559 | "build/netstandard2.0/ref/System.Diagnostics.Process.dll", 560 | "build/netstandard2.0/ref/System.Diagnostics.StackTrace.dll", 561 | "build/netstandard2.0/ref/System.Diagnostics.TextWriterTraceListener.dll", 562 | "build/netstandard2.0/ref/System.Diagnostics.Tools.dll", 563 | "build/netstandard2.0/ref/System.Diagnostics.TraceSource.dll", 564 | "build/netstandard2.0/ref/System.Diagnostics.Tracing.dll", 565 | "build/netstandard2.0/ref/System.Drawing.Primitives.dll", 566 | "build/netstandard2.0/ref/System.Drawing.dll", 567 | "build/netstandard2.0/ref/System.Dynamic.Runtime.dll", 568 | "build/netstandard2.0/ref/System.Globalization.Calendars.dll", 569 | "build/netstandard2.0/ref/System.Globalization.Extensions.dll", 570 | "build/netstandard2.0/ref/System.Globalization.dll", 571 | "build/netstandard2.0/ref/System.IO.Compression.FileSystem.dll", 572 | "build/netstandard2.0/ref/System.IO.Compression.ZipFile.dll", 573 | "build/netstandard2.0/ref/System.IO.Compression.dll", 574 | "build/netstandard2.0/ref/System.IO.FileSystem.DriveInfo.dll", 575 | "build/netstandard2.0/ref/System.IO.FileSystem.Primitives.dll", 576 | "build/netstandard2.0/ref/System.IO.FileSystem.Watcher.dll", 577 | "build/netstandard2.0/ref/System.IO.FileSystem.dll", 578 | "build/netstandard2.0/ref/System.IO.IsolatedStorage.dll", 579 | "build/netstandard2.0/ref/System.IO.MemoryMappedFiles.dll", 580 | "build/netstandard2.0/ref/System.IO.Pipes.dll", 581 | "build/netstandard2.0/ref/System.IO.UnmanagedMemoryStream.dll", 582 | "build/netstandard2.0/ref/System.IO.dll", 583 | "build/netstandard2.0/ref/System.Linq.Expressions.dll", 584 | "build/netstandard2.0/ref/System.Linq.Parallel.dll", 585 | "build/netstandard2.0/ref/System.Linq.Queryable.dll", 586 | "build/netstandard2.0/ref/System.Linq.dll", 587 | "build/netstandard2.0/ref/System.Net.Http.dll", 588 | "build/netstandard2.0/ref/System.Net.NameResolution.dll", 589 | "build/netstandard2.0/ref/System.Net.NetworkInformation.dll", 590 | "build/netstandard2.0/ref/System.Net.Ping.dll", 591 | "build/netstandard2.0/ref/System.Net.Primitives.dll", 592 | "build/netstandard2.0/ref/System.Net.Requests.dll", 593 | "build/netstandard2.0/ref/System.Net.Security.dll", 594 | "build/netstandard2.0/ref/System.Net.Sockets.dll", 595 | "build/netstandard2.0/ref/System.Net.WebHeaderCollection.dll", 596 | "build/netstandard2.0/ref/System.Net.WebSockets.Client.dll", 597 | "build/netstandard2.0/ref/System.Net.WebSockets.dll", 598 | "build/netstandard2.0/ref/System.Net.dll", 599 | "build/netstandard2.0/ref/System.Numerics.dll", 600 | "build/netstandard2.0/ref/System.ObjectModel.dll", 601 | "build/netstandard2.0/ref/System.Reflection.Extensions.dll", 602 | "build/netstandard2.0/ref/System.Reflection.Primitives.dll", 603 | "build/netstandard2.0/ref/System.Reflection.dll", 604 | "build/netstandard2.0/ref/System.Resources.Reader.dll", 605 | "build/netstandard2.0/ref/System.Resources.ResourceManager.dll", 606 | "build/netstandard2.0/ref/System.Resources.Writer.dll", 607 | "build/netstandard2.0/ref/System.Runtime.CompilerServices.VisualC.dll", 608 | "build/netstandard2.0/ref/System.Runtime.Extensions.dll", 609 | "build/netstandard2.0/ref/System.Runtime.Handles.dll", 610 | "build/netstandard2.0/ref/System.Runtime.InteropServices.RuntimeInformation.dll", 611 | "build/netstandard2.0/ref/System.Runtime.InteropServices.dll", 612 | "build/netstandard2.0/ref/System.Runtime.Numerics.dll", 613 | "build/netstandard2.0/ref/System.Runtime.Serialization.Formatters.dll", 614 | "build/netstandard2.0/ref/System.Runtime.Serialization.Json.dll", 615 | "build/netstandard2.0/ref/System.Runtime.Serialization.Primitives.dll", 616 | "build/netstandard2.0/ref/System.Runtime.Serialization.Xml.dll", 617 | "build/netstandard2.0/ref/System.Runtime.Serialization.dll", 618 | "build/netstandard2.0/ref/System.Runtime.dll", 619 | "build/netstandard2.0/ref/System.Security.Claims.dll", 620 | "build/netstandard2.0/ref/System.Security.Cryptography.Algorithms.dll", 621 | "build/netstandard2.0/ref/System.Security.Cryptography.Csp.dll", 622 | "build/netstandard2.0/ref/System.Security.Cryptography.Encoding.dll", 623 | "build/netstandard2.0/ref/System.Security.Cryptography.Primitives.dll", 624 | "build/netstandard2.0/ref/System.Security.Cryptography.X509Certificates.dll", 625 | "build/netstandard2.0/ref/System.Security.Principal.dll", 626 | "build/netstandard2.0/ref/System.Security.SecureString.dll", 627 | "build/netstandard2.0/ref/System.ServiceModel.Web.dll", 628 | "build/netstandard2.0/ref/System.Text.Encoding.Extensions.dll", 629 | "build/netstandard2.0/ref/System.Text.Encoding.dll", 630 | "build/netstandard2.0/ref/System.Text.RegularExpressions.dll", 631 | "build/netstandard2.0/ref/System.Threading.Overlapped.dll", 632 | "build/netstandard2.0/ref/System.Threading.Tasks.Parallel.dll", 633 | "build/netstandard2.0/ref/System.Threading.Tasks.dll", 634 | "build/netstandard2.0/ref/System.Threading.Thread.dll", 635 | "build/netstandard2.0/ref/System.Threading.ThreadPool.dll", 636 | "build/netstandard2.0/ref/System.Threading.Timer.dll", 637 | "build/netstandard2.0/ref/System.Threading.dll", 638 | "build/netstandard2.0/ref/System.Transactions.dll", 639 | "build/netstandard2.0/ref/System.ValueTuple.dll", 640 | "build/netstandard2.0/ref/System.Web.dll", 641 | "build/netstandard2.0/ref/System.Windows.dll", 642 | "build/netstandard2.0/ref/System.Xml.Linq.dll", 643 | "build/netstandard2.0/ref/System.Xml.ReaderWriter.dll", 644 | "build/netstandard2.0/ref/System.Xml.Serialization.dll", 645 | "build/netstandard2.0/ref/System.Xml.XDocument.dll", 646 | "build/netstandard2.0/ref/System.Xml.XPath.XDocument.dll", 647 | "build/netstandard2.0/ref/System.Xml.XPath.dll", 648 | "build/netstandard2.0/ref/System.Xml.XmlDocument.dll", 649 | "build/netstandard2.0/ref/System.Xml.XmlSerializer.dll", 650 | "build/netstandard2.0/ref/System.Xml.dll", 651 | "build/netstandard2.0/ref/System.dll", 652 | "build/netstandard2.0/ref/mscorlib.dll", 653 | "build/netstandard2.0/ref/netstandard.dll", 654 | "build/netstandard2.0/ref/netstandard.xml", 655 | "lib/netstandard1.0/_._", 656 | "netstandard.library.2.0.0.nupkg.sha512", 657 | "netstandard.library.nuspec" 658 | ] 659 | } 660 | }, 661 | "projectFileDependencyGroups": { 662 | ".NETCoreApp,Version=v2.0": [ 663 | "Microsoft.NETCore.App >= 2.0.0" 664 | ] 665 | }, 666 | "packageFolders": { 667 | "C:\\Users\\karan\\.nuget\\packages\\": {}, 668 | "C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder": {} 669 | }, 670 | "project": { 671 | "version": "1.0.0", 672 | "restore": { 673 | "projectUniqueName": "C:\\github\\sshserver\\sshserver.csproj", 674 | "projectName": "sshserver", 675 | "projectPath": "C:\\github\\sshserver\\sshserver.csproj", 676 | "packagesPath": "C:\\Users\\karan\\.nuget\\packages\\", 677 | "outputPath": "C:\\github\\sshserver\\obj\\", 678 | "projectStyle": "PackageReference", 679 | "fallbackFolders": [ 680 | "C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder" 681 | ], 682 | "configFilePaths": [ 683 | "C:\\Users\\karan\\AppData\\Roaming\\NuGet\\NuGet.Config", 684 | "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" 685 | ], 686 | "originalTargetFrameworks": [ 687 | "netcoreapp2.0" 688 | ], 689 | "sources": { 690 | "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, 691 | "https://api.nuget.org/v3/index.json": {} 692 | }, 693 | "frameworks": { 694 | "netcoreapp2.0": { 695 | "projectReferences": {} 696 | } 697 | }, 698 | "warningProperties": { 699 | "warnAsError": [ 700 | "NU1605" 701 | ] 702 | } 703 | }, 704 | "frameworks": { 705 | "netcoreapp2.0": { 706 | "dependencies": { 707 | "Microsoft.NETCore.App": { 708 | "target": "Package", 709 | "version": "[2.0.0, )", 710 | "autoReferenced": true 711 | } 712 | }, 713 | "imports": [ 714 | "net461" 715 | ], 716 | "assetTargetFallback": true, 717 | "warn": true 718 | } 719 | } 720 | } 721 | } -------------------------------------------------------------------------------- /obj/sshserver.csproj.nuget.cache: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "dgSpecHash": "UXMk2aB2SIj87GeRUKXWyfo13vRTgI49hqCG5zZ8TYUz6vPpXw0bNKlutaBBmcOb6hLDInJ4cqvQGJCHOoedog==", 4 | "success": true 5 | } -------------------------------------------------------------------------------- /obj/sshserver.csproj.nuget.g.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | True 5 | NuGet 6 | C:\github\sshserver\obj\project.assets.json 7 | $(UserProfile)\.nuget\packages\ 8 | C:\Users\karan\.nuget\packages\;C:\Program Files\dotnet\sdk\NuGetFallbackFolder 9 | PackageReference 10 | 4.5.0 11 | 12 | 13 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /obj/sshserver.csproj.nuget.g.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /sshserver.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /sshserver.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": true, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | }, 10 | "port": 22, 11 | "keys": { 12 | "ssh-rsa": "xXKzcIH/rzcfv2D7VcvLdxR5S5iw2TTsP65Aa82S4+9ZIqLPTNtuzr76Mz6Cx0yDOhawHlIujtalqaaQzaUvkudCtMcVMnj37OcCYz7XDAYejalCxf/vtJo7U4mnYdCM+nAOQKNIDKLGbLtGuEAGwdi560DOJY2plhnBf1oOI+k=AQAB

37kMr9YiU4cSqHqTJSjBJ/szG2O4n5xSIlPy4MZ4aAN5NALHxfsN0dq1y8NL6GTLMI5qoykvp4Bjrm2ZgU1cDQ==

4e84rF+UsFBfQEKJc2pbACWWJjttNW0hccdQZzA3IUxRmd/Z4yEMr1L70TP0XV7dw1RDs1JyU7xnXBIbGy5ETQ==vP0TbI6VnL3j0xMIrkFJOj8Ho0GQOrTQ5VLJP3wpRqR4hKk8nVBBEl+RZznpK73Jr5D/ICmwqezZSAYpwILbGQ==bHdzZtWwRYEgaXJIGL+7lnN1BT/MazTMNJpykEeGgBbqqgvcx/zq4RTezg26SEUuBANlSSbQukCeAoayurbYlQ==Irq9vR7CXIVR+r09caYIxIY8BOig+HShN1bXvATERJcjTW2jUgJrUttDGNEx70/hBd7m1NWCZz5YO3RH9Bdf5w==AqxsufxFcW9TDCAmQK4mwVdsoQjRp2jfcULmkM8fl9u40dtxTr6Csv5dz7qfKLWxHTGlDUDabCK2t/DCcZZoA3rsqwLADe4ZerDdg6xiq4MBzNprM8Y0IfNESEdFB9T0T73ONQCsMalUzEvUknC4Ed4Fya34LUHntgQtEhXpDJE=
" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sshserver.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "sshserver", "sshserver.csproj", "{3BE81994-C03C-4A9C-9E23-EF7BE4B9CA92}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Debug|x86 = Debug|x86 13 | Release|Any CPU = Release|Any CPU 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {3BE81994-C03C-4A9C-9E23-EF7BE4B9CA92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {3BE81994-C03C-4A9C-9E23-EF7BE4B9CA92}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {3BE81994-C03C-4A9C-9E23-EF7BE4B9CA92}.Debug|x64.ActiveCfg = Debug|x64 24 | {3BE81994-C03C-4A9C-9E23-EF7BE4B9CA92}.Debug|x64.Build.0 = Debug|x64 25 | {3BE81994-C03C-4A9C-9E23-EF7BE4B9CA92}.Debug|x86.ActiveCfg = Debug|x86 26 | {3BE81994-C03C-4A9C-9E23-EF7BE4B9CA92}.Debug|x86.Build.0 = Debug|x86 27 | {3BE81994-C03C-4A9C-9E23-EF7BE4B9CA92}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {3BE81994-C03C-4A9C-9E23-EF7BE4B9CA92}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {3BE81994-C03C-4A9C-9E23-EF7BE4B9CA92}.Release|x64.ActiveCfg = Release|x64 30 | {3BE81994-C03C-4A9C-9E23-EF7BE4B9CA92}.Release|x64.Build.0 = Release|x64 31 | {3BE81994-C03C-4A9C-9E23-EF7BE4B9CA92}.Release|x86.ActiveCfg = Release|x86 32 | {3BE81994-C03C-4A9C-9E23-EF7BE4B9CA92}.Release|x86.Build.0 = Release|x86 33 | EndGlobalSection 34 | EndGlobal 35 | --------------------------------------------------------------------------------