diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs index 780b7918..a8317909 100644 --- a/src/Renci.SshNet/ConnectionInfo.cs +++ b/src/Renci.SshNet/ConnectionInfo.cs @@ -322,6 +322,8 @@ namespace Renci.SshNet KeyExchangeAlgorithms = new Dictionary { + {"curve25519-sha256", typeof(KeyExchangeECCurve25519)}, + {"curve25519-sha256@libssh.org", typeof(KeyExchangeECCurve25519)}, {"ecdh-sha2-nistp256", typeof(KeyExchangeECDH256)}, {"ecdh-sha2-nistp384", typeof(KeyExchangeECDH384)}, {"ecdh-sha2-nistp521", typeof(KeyExchangeECDH521)}, diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj index e1bb56ba..62508016 100644 --- a/src/Renci.SshNet/Renci.SshNet.csproj +++ b/src/Renci.SshNet/Renci.SshNet.csproj @@ -310,6 +310,7 @@ + diff --git a/src/Renci.SshNet/Security/KeyExchangeECCurve25519.cs b/src/Renci.SshNet/Security/KeyExchangeECCurve25519.cs new file mode 100644 index 00000000..39658de0 --- /dev/null +++ b/src/Renci.SshNet/Security/KeyExchangeECCurve25519.cs @@ -0,0 +1,99 @@ +using System; +using Renci.SshNet.Common; +using Renci.SshNet.Messages.Transport; +using Renci.SshNet.Security.Chaos.NaCl; +using Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10; + +namespace Renci.SshNet.Security +{ + internal class KeyExchangeECCurve25519 : KeyExchangeEC + { + private byte[] _privateKey; + + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "curve25519-sha256"; } + } + + /// + /// Gets the size, in bits, of the computed hash code. + /// + /// + /// The size, in bits, of the computed hash code. + /// + protected override int HashSize + { + get { return 256; } + } + + /// + /// Starts key exchange algorithm + /// + /// The session. + /// Key exchange init message. + public override void Start(Session session, KeyExchangeInitMessage message) + { + base.Start(session, message); + + Session.RegisterMessage("SSH_MSG_KEX_ECDH_REPLY"); + + Session.KeyExchangeEcdhReplyMessageReceived += Session_KeyExchangeEcdhReplyMessageReceived; + + var basepoint = new byte[MontgomeryCurve25519.PublicKeySizeInBytes]; + basepoint[0] = 9; + + var rnd = new Random(); + _privateKey = new byte[MontgomeryCurve25519.PrivateKeySizeInBytes]; + rnd.NextBytes(_privateKey); + + _clientExchangeValue = new byte[MontgomeryCurve25519.PublicKeySizeInBytes]; + MontgomeryOperations.scalarmult(_clientExchangeValue, 0, _privateKey, 0, basepoint, 0); + + SendMessage(new KeyExchangeEcdhInitMessage(_clientExchangeValue)); + } + + /// + /// Finishes key exchange algorithm. + /// + public override void Finish() + { + base.Finish(); + + Session.KeyExchangeEcdhReplyMessageReceived -= Session_KeyExchangeEcdhReplyMessageReceived; + } + + private void Session_KeyExchangeEcdhReplyMessageReceived(object sender, MessageEventArgs e) + { + var message = e.Message; + + // Unregister message once received + Session.UnRegisterMessage("SSH_MSG_KEX_ECDH_REPLY"); + + HandleServerEcdhReply(message.KS, message.QS, message.Signature); + + // When SSH_MSG_KEXDH_REPLY received key exchange is completed + Finish(); + } + + /// + /// Handles the server DH reply message. + /// + /// The host key. + /// The server exchange value. + /// The signature. + protected override void HandleServerEcdhReply(byte[] hostKey, byte[] serverExchangeValue, byte[] signature) + { + _serverExchangeValue = serverExchangeValue; + _hostKey = hostKey; + _serverExchangeValue = serverExchangeValue; + _signature = signature; + + var sharedKey = new byte[MontgomeryCurve25519.PublicKeySizeInBytes]; + MontgomeryOperations.scalarmult(sharedKey, 0, _privateKey, 0, serverExchangeValue, 0); + SharedKey = sharedKey.ToBigInteger2(); + } + } +}