using System; using System.Numerics; using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; namespace Renci.SshNet.Security { /// /// Represents base class for Diffie Hellman key exchange algorithm. /// internal abstract class KeyExchangeDiffieHellman : KeyExchange { #pragma warning disable SA1401 // Fields should be private /// /// 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 byte[] _clientExchangeValue; /// /// Specifies server exchange number. /// protected byte[] _serverExchangeValue; /// /// Specifies random generated number. /// protected BigInteger _privateExponent; /// /// Specifies host key data. /// protected byte[] _hostKey; /// /// Specifies signature data. /// protected byte[] _signature; #pragma warning restore SA1401 // Fields should be private /// /// 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() { return ValidateExchangeHash(_hostKey, _signature); } /// public override void Start(Session session, KeyExchangeInitMessage message, bool sendClientInitMessage) { base.Start(session, message, sendClientInitMessage); _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); BigInteger clientExchangeValue; do { // Create private component _privateExponent = RandomBigInt(privateExponentSize); // Generate public component clientExchangeValue = BigInteger.ModPow(_group, _privateExponent, _prime); } while (clientExchangeValue < 1 || clientExchangeValue > (_prime - 1)); _clientExchangeValue = clientExchangeValue.ToByteArray(isBigEndian: true); } /// /// Generates a new, random of the specified length. /// /// The number of bits for the new number. /// A random number of the specified length. private static BigInteger RandomBigInt(int bitLength) { var bytesArray = CryptoAbstraction.GenerateRandom((bitLength / 8) + (((bitLength % 8) > 0) ? 1 : 0)); bytesArray[bytesArray.Length - 1] = (byte)(bytesArray[bytesArray.Length - 1] & 0x7F); // Ensure not a negative value return new BigInteger(bytesArray); } /// /// Handles the server DH reply message. /// /// The host key. /// The server exchange value. /// The signature. protected virtual void HandleServerDhReply(byte[] hostKey, byte[] serverExchangeValue, byte[] signature) { _serverExchangeValue = serverExchangeValue; _hostKey = hostKey; SharedKey = BigInteger.ModPow(serverExchangeValue.ToBigInteger(), _privateExponent, _prime).ToByteArray(isBigEndian: true); _signature = signature; } } }