|
|
@@ -19,6 +19,7 @@ namespace Renci.SshNet.Security
|
|
|
public abstract class KeyExchange : Algorithm, IKeyExchange
|
|
|
{
|
|
|
private readonly ILogger _logger;
|
|
|
+ private Func<byte[], KeyHostAlgorithm> _hostKeyAlgorithmFactory;
|
|
|
private CipherInfo _clientCipherInfo;
|
|
|
private CipherInfo _serverCipherInfo;
|
|
|
private HashInfo _clientHashInfo;
|
|
|
@@ -81,6 +82,33 @@ namespace Renci.SshNet.Security
|
|
|
SendMessage(session.ClientInitMessage);
|
|
|
}
|
|
|
|
|
|
+ // Determine host key algorithm
|
|
|
+ var hostKeyAlgorithmName = (from b in session.ConnectionInfo.HostKeyAlgorithms.Keys
|
|
|
+ from a in message.ServerHostKeyAlgorithms
|
|
|
+ where a == b
|
|
|
+ select a).FirstOrDefault();
|
|
|
+
|
|
|
+ if (_logger.IsEnabled(LogLevel.Trace))
|
|
|
+ {
|
|
|
+ _logger.LogTrace("[{SessionId}] Host key algorithm: we offer {WeOffer}",
|
|
|
+ Session.SessionIdHex,
|
|
|
+ session.ConnectionInfo.HostKeyAlgorithms.Keys.Join(","));
|
|
|
+
|
|
|
+ _logger.LogTrace("[{SessionId}] Host key algorithm: they offer {TheyOffer}",
|
|
|
+ Session.SessionIdHex,
|
|
|
+ message.ServerHostKeyAlgorithms.Join(","));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (hostKeyAlgorithmName is null)
|
|
|
+ {
|
|
|
+ throw new SshConnectionException(
|
|
|
+ $"No matching host key algorithm (server offers {message.ServerHostKeyAlgorithms.Join(",")})",
|
|
|
+ DisconnectReason.KeyExchangeFailed);
|
|
|
+ }
|
|
|
+
|
|
|
+ session.ConnectionInfo.CurrentHostKeyAlgorithm = hostKeyAlgorithmName;
|
|
|
+ _hostKeyAlgorithmFactory = session.ConnectionInfo.HostKeyAlgorithms[hostKeyAlgorithmName];
|
|
|
+
|
|
|
// Determine client encryption algorithm
|
|
|
var clientEncryptionAlgorithmName = (from b in session.ConnectionInfo.Encryptions.Keys
|
|
|
from a in message.EncryptionAlgorithmsClientToServer
|
|
|
@@ -98,9 +126,11 @@ namespace Renci.SshNet.Security
|
|
|
message.EncryptionAlgorithmsClientToServer.Join(","));
|
|
|
}
|
|
|
|
|
|
- if (string.IsNullOrEmpty(clientEncryptionAlgorithmName))
|
|
|
+ if (clientEncryptionAlgorithmName is null)
|
|
|
{
|
|
|
- throw new SshConnectionException("Client encryption algorithm not found", DisconnectReason.KeyExchangeFailed);
|
|
|
+ throw new SshConnectionException(
|
|
|
+ $"No matching client encryption algorithm (server offers {message.EncryptionAlgorithmsClientToServer.Join(",")})",
|
|
|
+ DisconnectReason.KeyExchangeFailed);
|
|
|
}
|
|
|
|
|
|
session.ConnectionInfo.CurrentClientEncryption = clientEncryptionAlgorithmName;
|
|
|
@@ -123,9 +153,11 @@ namespace Renci.SshNet.Security
|
|
|
message.EncryptionAlgorithmsServerToClient.Join(","));
|
|
|
}
|
|
|
|
|
|
- if (string.IsNullOrEmpty(serverDecryptionAlgorithmName))
|
|
|
+ if (serverDecryptionAlgorithmName is null)
|
|
|
{
|
|
|
- throw new SshConnectionException("Server decryption algorithm not found", DisconnectReason.KeyExchangeFailed);
|
|
|
+ throw new SshConnectionException(
|
|
|
+ $"No matching server encryption algorithm (server offers {message.EncryptionAlgorithmsServerToClient.Join(",")})",
|
|
|
+ DisconnectReason.KeyExchangeFailed);
|
|
|
}
|
|
|
|
|
|
session.ConnectionInfo.CurrentServerEncryption = serverDecryptionAlgorithmName;
|
|
|
@@ -150,9 +182,11 @@ namespace Renci.SshNet.Security
|
|
|
message.MacAlgorithmsClientToServer.Join(","));
|
|
|
}
|
|
|
|
|
|
- if (string.IsNullOrEmpty(clientHmacAlgorithmName))
|
|
|
+ if (clientHmacAlgorithmName is null)
|
|
|
{
|
|
|
- throw new SshConnectionException("Client HMAC algorithm not found", DisconnectReason.KeyExchangeFailed);
|
|
|
+ throw new SshConnectionException(
|
|
|
+ $"No matching client MAC algorithm (server offers {message.MacAlgorithmsClientToServer.Join(",")})",
|
|
|
+ DisconnectReason.KeyExchangeFailed);
|
|
|
}
|
|
|
|
|
|
session.ConnectionInfo.CurrentClientHmacAlgorithm = clientHmacAlgorithmName;
|
|
|
@@ -178,9 +212,11 @@ namespace Renci.SshNet.Security
|
|
|
message.MacAlgorithmsServerToClient.Join(","));
|
|
|
}
|
|
|
|
|
|
- if (string.IsNullOrEmpty(serverHmacAlgorithmName))
|
|
|
+ if (serverHmacAlgorithmName is null)
|
|
|
{
|
|
|
- throw new SshConnectionException("Server HMAC algorithm not found", DisconnectReason.KeyExchangeFailed);
|
|
|
+ throw new SshConnectionException(
|
|
|
+ $"No matching server MAC algorithm (server offers {message.MacAlgorithmsServerToClient.Join(",")})",
|
|
|
+ DisconnectReason.KeyExchangeFailed);
|
|
|
}
|
|
|
|
|
|
session.ConnectionInfo.CurrentServerHmacAlgorithm = serverHmacAlgorithmName;
|
|
|
@@ -204,9 +240,11 @@ namespace Renci.SshNet.Security
|
|
|
message.CompressionAlgorithmsClientToServer.Join(","));
|
|
|
}
|
|
|
|
|
|
- if (string.IsNullOrEmpty(compressionAlgorithmName))
|
|
|
+ if (compressionAlgorithmName is null)
|
|
|
{
|
|
|
- throw new SshConnectionException("Compression algorithm not found", DisconnectReason.KeyExchangeFailed);
|
|
|
+ throw new SshConnectionException(
|
|
|
+ $"No matching client compression algorithm (server offers {message.CompressionAlgorithmsClientToServer.Join(",")})",
|
|
|
+ DisconnectReason.KeyExchangeFailed);
|
|
|
}
|
|
|
|
|
|
session.ConnectionInfo.CurrentClientCompressionAlgorithm = compressionAlgorithmName;
|
|
|
@@ -229,9 +267,11 @@ namespace Renci.SshNet.Security
|
|
|
message.CompressionAlgorithmsServerToClient.Join(","));
|
|
|
}
|
|
|
|
|
|
- if (string.IsNullOrEmpty(decompressionAlgorithmName))
|
|
|
+ if (decompressionAlgorithmName is null)
|
|
|
{
|
|
|
- throw new SshConnectionException("Decompression algorithm not found", DisconnectReason.KeyExchangeFailed);
|
|
|
+ throw new SshConnectionException(
|
|
|
+ $"No matching server compression algorithm (server offers {message.CompressionAlgorithmsServerToClient.Join(",")})",
|
|
|
+ DisconnectReason.KeyExchangeFailed);
|
|
|
}
|
|
|
|
|
|
session.ConnectionInfo.CurrentServerCompressionAlgorithm = decompressionAlgorithmName;
|
|
|
@@ -245,7 +285,7 @@ namespace Renci.SshNet.Security
|
|
|
{
|
|
|
if (!ValidateExchangeHash())
|
|
|
{
|
|
|
- throw new SshConnectionException("Key exchange negotiation failed.", DisconnectReason.KeyExchangeFailed);
|
|
|
+ throw new SshConnectionException("Host key could not be verified.", DisconnectReason.KeyExchangeFailed);
|
|
|
}
|
|
|
|
|
|
SendMessage(new NewKeysMessage());
|
|
|
@@ -449,40 +489,9 @@ namespace Renci.SshNet.Security
|
|
|
{
|
|
|
var exchangeHash = CalculateHash();
|
|
|
|
|
|
- // We need to inspect both the key and signature format identifers to find the correct
|
|
|
- // HostAlgorithm instance. Example cases:
|
|
|
-
|
|
|
- // Key identifier Signature identifier | Algorithm name
|
|
|
- // ssh-rsa ssh-rsa | ssh-rsa
|
|
|
- // ssh-rsa rsa-sha2-256 | rsa-sha2-256
|
|
|
- // ssh-rsa-cert-v01@openssh.com ssh-rsa | ssh-rsa-cert-v01@openssh.com
|
|
|
- // ssh-rsa-cert-v01@openssh.com rsa-sha2-256 | rsa-sha2-256-cert-v01@openssh.com
|
|
|
-
|
|
|
- var signatureData = new KeyHostAlgorithm.SignatureKeyData();
|
|
|
- signatureData.Load(encodedSignature);
|
|
|
-
|
|
|
- string keyName;
|
|
|
- using (var keyReader = new SshDataStream(encodedKey))
|
|
|
- {
|
|
|
- keyName = keyReader.ReadString();
|
|
|
- }
|
|
|
-
|
|
|
- string algorithmName;
|
|
|
-
|
|
|
- if (signatureData.AlgorithmName.StartsWith("rsa-sha2", StringComparison.Ordinal))
|
|
|
- {
|
|
|
- algorithmName = keyName.Replace("ssh-rsa", signatureData.AlgorithmName);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- algorithmName = keyName;
|
|
|
- }
|
|
|
-
|
|
|
- var keyAlgorithm = Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](encodedKey);
|
|
|
-
|
|
|
- Session.ConnectionInfo.CurrentHostKeyAlgorithm = algorithmName;
|
|
|
+ var keyAlgorithm = _hostKeyAlgorithmFactory(encodedKey);
|
|
|
|
|
|
- return keyAlgorithm.VerifySignatureBlob(exchangeHash, signatureData.Signature) && CanTrustHostKey(keyAlgorithm);
|
|
|
+ return keyAlgorithm.VerifySignature(exchangeHash, encodedSignature) && CanTrustHostKey(keyAlgorithm);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|