using Org.BouncyCastle.Crypto.Agreement;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Transport;
namespace Renci.SshNet.Security
{
internal sealed class KeyExchangeECCurve25519 : KeyExchangeEC
{
private X25519Agreement _keyAgreement;
///
/// 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; }
}
///
public override void Start(Session session, KeyExchangeInitMessage message, bool sendClientInitMessage)
{
base.Start(session, message, sendClientInitMessage);
Session.RegisterMessage("SSH_MSG_KEX_ECDH_REPLY");
Session.KeyExchangeEcdhReplyMessageReceived += Session_KeyExchangeEcdhReplyMessageReceived;
var g = new X25519KeyPairGenerator();
g.Init(new X25519KeyGenerationParameters(CryptoAbstraction.SecureRandom));
var aKeyPair = g.GenerateKeyPair();
_keyAgreement = new X25519Agreement();
_keyAgreement.Init(aKeyPair.Private);
_clientExchangeValue = ((X25519PublicKeyParameters)aKeyPair.Public).GetEncoded();
SendMessage(new KeyExchangeEcdhInitMessage(_clientExchangeValue));
}
///
/// Finishes key exchange algorithm.
///
public override void Finish()
{
base.Finish();
Session.KeyExchangeEcdhReplyMessageReceived -= Session_KeyExchangeEcdhReplyMessageReceived;
}
///
/// Hashes the specified data bytes.
///
/// The hash data.
///
/// The hash of the data.
///
protected override byte[] Hash(byte[] hashData)
{
return CryptoAbstraction.HashSHA256(hashData);
}
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.
private void HandleServerEcdhReply(byte[] hostKey, byte[] serverExchangeValue, byte[] signature)
{
_serverExchangeValue = serverExchangeValue;
_hostKey = hostKey;
_signature = signature;
var publicKey = new X25519PublicKeyParameters(serverExchangeValue);
var k1 = new byte[_keyAgreement.AgreementSize];
_keyAgreement.CalculateAgreement(publicKey, k1, 0);
SharedKey = k1.ToBigInteger2().ToByteArray(isBigEndian: true);
}
}
}