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;
}
}
}