| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437 | using System;using System.Collections.Generic;using System.Linq;using System.Security.Cryptography;using Renci.SshNet.Common;using Renci.SshNet.Compression;using Renci.SshNet.Messages;using Renci.SshNet.Messages.Transport;using Renci.SshNet.Security.Cryptography.Ciphers;using Renci.SshNet.Security.Cryptography;namespace Renci.SshNet.Security{    /// <summary>    /// Represents base class for different key exchange algorithm implementations    /// </summary>    public abstract class KeyExchange : Algorithm, IDisposable    {        private CipherInfo _clientCipherInfo;        private CipherInfo _serverCipherInfo;        private Func<byte[], HashAlgorithm> _cientHmacAlgorithmType;        private Func<byte[], HashAlgorithm> _serverHmacAlgorithmType;        private Type _compressionType;        private Type _decompressionType;        /// <summary>        /// Gets or sets the session.        /// </summary>        /// <value>        /// The session.        /// </value>        protected Session Session { get; private set; }        /// <summary>        /// Gets or sets key exchange shared key.        /// </summary>        /// <value>        /// The shared key.        /// </value>        public BigInteger SharedKey { get; protected set; }        private byte[] _exchangeHash;        /// <summary>        /// Gets the exchange hash.        /// </summary>        /// <value>The exchange hash.</value>        public byte[] ExchangeHash        {            get            {                if (this._exchangeHash == null)                {                    this._exchangeHash = this.CalculateHash();                }                return this._exchangeHash;            }        }        /// <summary>        /// Occurs when host key received.        /// </summary>        public event EventHandler<HostKeyEventArgs> HostKeyReceived;        /// <summary>        /// Starts key exchange algorithm        /// </summary>        /// <param name="session">The session.</param>        /// <param name="message">Key exchange init message.</param>        public virtual void Start(Session session, KeyExchangeInitMessage message)        {            this.Session = session;            this.SendMessage(session.ClientInitMessage);            //  Determine encryption algorithm            var clientEncryptionAlgorithmName = (from b in session.ConnectionInfo.Encryptions.Keys                                                 from a in message.EncryptionAlgorithmsClientToServer                                                 where a == b                                                 select a).FirstOrDefault();            if (string.IsNullOrEmpty(clientEncryptionAlgorithmName))            {                throw new SshConnectionException("Client encryption algorithm not found", DisconnectReason.KeyExchangeFailed);            }            //  Determine encryption algorithm            var serverDecryptionAlgorithmName = (from b in session.ConnectionInfo.Encryptions.Keys                                                 from a in message.EncryptionAlgorithmsServerToClient                                                 where a == b                                                 select a).FirstOrDefault();            if (string.IsNullOrEmpty(serverDecryptionAlgorithmName))            {                throw new SshConnectionException("Server decryption algorithm not found", DisconnectReason.KeyExchangeFailed);            }            //  Determine client hmac algorithm            var clientHmacAlgorithmName = (from b in session.ConnectionInfo.HmacAlgorithms.Keys                                           from a in message.MacAlgorithmsClientToServer                                           where a == b                                           select a).FirstOrDefault();            if (string.IsNullOrEmpty(clientHmacAlgorithmName))            {                throw new SshConnectionException("Server HMAC algorithm not found", DisconnectReason.KeyExchangeFailed);            }            //  Determine server hmac algorithm            var serverHmacAlgorithmName = (from b in session.ConnectionInfo.HmacAlgorithms.Keys                                           from a in message.MacAlgorithmsServerToClient                                           where a == b                                           select a).FirstOrDefault();            if (string.IsNullOrEmpty(serverHmacAlgorithmName))            {                throw new SshConnectionException("Server HMAC algorithm not found", DisconnectReason.KeyExchangeFailed);            }            //  Determine compression algorithm            var compressionAlgorithmName = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys                                            from a in message.CompressionAlgorithmsClientToServer                                            where a == b                                            select a).FirstOrDefault();            if (string.IsNullOrEmpty(compressionAlgorithmName))            {                throw new SshConnectionException("Compression algorithm not found", DisconnectReason.KeyExchangeFailed);            }            //  Determine decompression algorithm            var decompressionAlgorithmName = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys                                              from a in message.CompressionAlgorithmsServerToClient                                              where a == b                                              select a).FirstOrDefault();            if (string.IsNullOrEmpty(decompressionAlgorithmName))            {                throw new SshConnectionException("Decompression algorithm not found", DisconnectReason.KeyExchangeFailed);            }            this._clientCipherInfo = session.ConnectionInfo.Encryptions[clientEncryptionAlgorithmName];            this._serverCipherInfo = session.ConnectionInfo.Encryptions[clientEncryptionAlgorithmName];            this._cientHmacAlgorithmType = session.ConnectionInfo.HmacAlgorithms[clientHmacAlgorithmName];            this._serverHmacAlgorithmType = session.ConnectionInfo.HmacAlgorithms[serverHmacAlgorithmName];            this._compressionType = session.ConnectionInfo.CompressionAlgorithms[compressionAlgorithmName];            this._decompressionType = session.ConnectionInfo.CompressionAlgorithms[decompressionAlgorithmName];        }        /// <summary>        /// Finishes key exchange algorithm.        /// </summary>        public virtual void Finish()        {            //  Validate hash            if (this.ValidateExchangeHash())            {                this.SendMessage(new NewKeysMessage());            }            else            {                throw new SshConnectionException("Key exchange negotiation failed.", DisconnectReason.KeyExchangeFailed);            }        }        /// <summary>        /// Creates the server side cipher to use.        /// </summary>        /// <returns></returns>        public BlockCipher CreateServerCipher()        {            //  Resolve Session ID            var sessionId = this.Session.SessionId ?? this.ExchangeHash;            //  Calculate server to client initial IV            var serverVector = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'B', sessionId));            //  Calculate server to client encryption            var serverKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'D', sessionId));            serverKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, serverKey, this._serverCipherInfo.KeySize / 8);                        //  Create server cipher            return this._serverCipherInfo.Cipher(serverKey, serverVector);        }        /// <summary>        /// Creates the client side cipher to use.        /// </summary>        /// <returns></returns>        public BlockCipher CreateClientCipher()        {            //  Resolve Session ID            var sessionId = this.Session.SessionId ?? this.ExchangeHash;            //  Calculate client to server initial IV            var clientVector = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'A', sessionId));            //  Calculate client to server encryption            var clientKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'C', sessionId));            clientKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, clientKey, this._clientCipherInfo.KeySize / 8);            //  Create client cipher            return this._clientCipherInfo.Cipher(clientKey, clientVector);        }        /// <summary>        /// Creates the server side hash algorithm to use.        /// </summary>        /// <returns></returns>        public HashAlgorithm CreateServerHash()        {            //  Resolve Session ID            var sessionId = this.Session.SessionId ?? this.ExchangeHash;            //return serverHMac;            return this._serverHmacAlgorithmType(this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'F', sessionId)));        }        /// <summary>        /// Creates the client side hash algorithm to use.        /// </summary>        /// <returns></returns>        public HashAlgorithm CreateClientHash()        {            //  Resolve Session ID            var sessionId = this.Session.SessionId ?? this.ExchangeHash;            //return clientHMac;            return this._cientHmacAlgorithmType(this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'E', sessionId)));        }        /// <summary>        /// Creates the compression algorithm to use to deflate data.        /// </summary>        /// <returns></returns>        public Compressor CreateCompressor()        {            if (this._compressionType == null)                return null;            var compressor = this._compressionType.CreateInstance<Compressor>();            compressor.Init(this.Session);            return compressor;        }        /// <summary>        /// Creates the compression algorithm to use to inflate data.        /// </summary>        /// <returns></returns>        public Compressor CreateDecompressor()        {            if (this._compressionType == null)                return null;            var decompressor = this._decompressionType.CreateInstance<Compressor>();            decompressor.Init(this.Session);            return decompressor;        }        /// <summary>        /// Determines whether the specified host key can be trusted.        /// </summary>        /// <param name="hostKey">The host key.</param>        /// <returns>        ///   <c>true</c> if the specified host key can be trusted; otherwise, <c>false</c>.        /// </returns>        protected bool CanTrustHostKey(byte[] hostKey)        {            var args = new HostKeyEventArgs(hostKey);            if (this.HostKeyReceived != null)            {                this.HostKeyReceived(this, args);            }            return args.CanTrust;        }        /// <summary>        /// Validates the exchange hash.        /// </summary>        /// <returns>true if exchange hash is valid; otherwise false.</returns>        protected abstract bool ValidateExchangeHash();        /// <summary>        /// Calculates key exchange hash value.        /// </summary>        /// <returns>Key exchange hash.</returns>        protected abstract byte[] CalculateHash();        /// <summary>        /// Hashes the specified data bytes.        /// </summary>        /// <param name="hashData">The hash data.</param>        /// <returns>        /// Hashed bytes        /// </returns>        protected virtual byte[] Hash(byte[] hashData)        {            using (var sha1 = new Renci.SshNet.Security.Cryptography.SHA1Hash())            {                return sha1.ComputeHash(hashData, 0, hashData.Length);            }        }        /// <summary>        /// Sends SSH message to the server        /// </summary>        /// <param name="message">The message.</param>        protected void SendMessage(Message message)        {            this.Session.SendMessage(message);        }        /// <summary>        /// Generates the session key.        /// </summary>        /// <param name="sharedKey">The shared key.</param>        /// <param name="exchangeHash">The exchange hash.</param>        /// <param name="key">The key.</param>        /// <param name="size">The size.</param>        /// <returns></returns>        private byte[] GenerateSessionKey(BigInteger sharedKey, byte[] exchangeHash, byte[] key, int size)        {            var result = new List<byte>(key);            while (size > result.Count)            {                result.AddRange(this.Hash(new _SessionKeyAdjustment                {                    SharedKey = sharedKey,                    ExcahngeHash = exchangeHash,                    Key = key,                }.GetBytes()));            }            return result.ToArray();        }        /// <summary>        /// Generates the session key.        /// </summary>        /// <param name="sharedKey">The shared key.</param>        /// <param name="exchangeHash">The exchange hash.</param>        /// <param name="p">The p.</param>        /// <param name="sessionId">The session id.</param>        /// <returns></returns>        private byte[] GenerateSessionKey(BigInteger sharedKey, byte[] exchangeHash, char p, byte[] sessionId)        {            return new _SessionKeyGeneration            {                SharedKey = sharedKey,                ExchangeHash = exchangeHash,                Char = p,                SessionId = sessionId,            }.GetBytes();        }        private class _SessionKeyGeneration : SshData        {            public BigInteger SharedKey { get; set; }            public byte[] ExchangeHash { get; set; }            public char Char { get; set; }            public byte[] SessionId { get; set; }            protected override void LoadData()            {                throw new NotImplementedException();            }            protected override void SaveData()            {                this.Write(this.SharedKey);                this.Write(this.ExchangeHash);                this.Write((byte)this.Char);                this.Write(this.SessionId);            }        }        private class _SessionKeyAdjustment : SshData        {            public BigInteger SharedKey { get; set; }            public byte[] ExcahngeHash { get; set; }            public byte[] Key { get; set; }            protected override void LoadData()            {                throw new NotImplementedException();            }            protected override void SaveData()            {                this.Write(this.SharedKey);                this.Write(this.ExcahngeHash);                this.Write(this.Key);            }        }        #region IDisposable Members        /// <summary>        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages.        /// </summary>        public void Dispose()        {            Dispose(true);            GC.SuppressFinalize(this);        }        /// <summary>        /// Releases unmanaged and - optionally - managed resources        /// </summary>        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged ResourceMessages.</param>        protected virtual void Dispose(bool disposing)        {        }        /// <summary>        /// Releases unmanaged resources and performs other cleanup operations before the        /// <see cref="KeyExchange"/> is reclaimed by garbage collection.        /// </summary>        ~KeyExchange()        {            // Do not re-create Dispose clean-up code here.            // Calling Dispose(false) is optimal in terms of            // readability and maintainability.            Dispose(false);        }        #endregion    }}
 |