using System;
using System.Text;
using Renci.SshNet.Messages.Transport;
using Renci.SshNet.Common;
namespace Renci.SshNet.Security
{
    /// 
    /// Represents base class for Diffie Hellman key exchange algorithm
    /// 
    internal abstract class KeyExchangeDiffieHellman : KeyExchange
    {
        /// 
        /// Specifies key exchange group number.
        /// 
        protected BigInteger _group;
        /// 
        /// Specifies key exchange prime number.
        /// 
        protected BigInteger _prime;
        /// 
        /// Specifies client payload
        /// 
        protected byte[] _clientPayload;
        /// 
        /// Specifies server payload
        /// 
        protected byte[] _serverPayload;
        /// 
        /// Specifies client exchange number.
        /// 
        protected BigInteger _clientExchangeValue;
        /// 
        /// Specifies server exchange number.
        /// 
        protected BigInteger _serverExchangeValue;
        /// 
        /// Specifies random generated number.
        /// 
        protected BigInteger _privateExponent;
        /// 
        /// Specifies host key data.
        /// 
        protected byte[] _hostKey;
        /// 
        /// Specifies signature data.
        /// 
        protected byte[] _signature;
        /// 
        /// Gets the size, in bits, of the computed hash code.
        /// 
        /// 
        /// The size, in bits, of the computed hash code.
        /// 
        protected abstract int HashSize { get; }
        /// 
        /// Validates the exchange hash.
        /// 
        /// 
        /// true if exchange hash is valid; otherwise false.
        /// 
        protected override bool ValidateExchangeHash()
        {
            var exchangeHash = CalculateHash();
            var length = Pack.BigEndianToUInt32(_hostKey);
            var algorithmName = Encoding.UTF8.GetString(_hostKey, 4, (int)length);
            var key = Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](_hostKey);
            Session.ConnectionInfo.CurrentHostKeyAlgorithm = algorithmName;
            if (CanTrustHostKey(key))
            {
                return key.VerifySignature(exchangeHash, _signature);
            }
            return false;
        }
        /// 
        /// Starts key exchange algorithm
        /// 
        /// The session.
        /// Key exchange init message.
        public override void Start(Session session, KeyExchangeInitMessage message)
        {
            base.Start(session, message);
            _serverPayload = message.GetBytes();
            _clientPayload = Session.ClientInitMessage.GetBytes();
        }
        /// 
        /// Populates the client exchange value.
        /// 
        protected void PopulateClientExchangeValue()
        {
            if (_group.IsZero)
                throw new ArgumentNullException("_group");
            if (_prime.IsZero)
                throw new ArgumentNullException("_prime");
            // generate private exponent that is twice the hash size (RFC 4419) with a minimum
            // of 1024 bits (whatever is less)
            var privateExponentSize = Math.Max(HashSize * 2, 1024);
            do
            {
                // create private component
                _privateExponent = BigInteger.Random(privateExponentSize);
                // generate public component
                _clientExchangeValue = BigInteger.ModPow(_group, _privateExponent, _prime);
            } while (_clientExchangeValue < 1 || _clientExchangeValue > (_prime - 1));
        }
        /// 
        /// Handles the server DH reply message.
        /// 
        /// The host key.
        /// The server exchange value.
        /// The signature.
        protected virtual void HandleServerDhReply(byte[] hostKey, BigInteger serverExchangeValue, byte[] signature)
        {
            _serverExchangeValue = serverExchangeValue;
            _hostKey = hostKey;
            SharedKey = BigInteger.ModPow(serverExchangeValue, _privateExponent, _prime);
            _signature = signature;
        }
    }
}