using System;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Transport;
using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9;
using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Agreement;
using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Generators;
using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Parameters;
using Renci.SshNet.Security.Org.BouncyCastle.Math.EC;
using Renci.SshNet.Security.Org.BouncyCastle.Security;
namespace Renci.SshNet.Security
{
internal abstract class KeyExchangeECDH : KeyExchangeEC
{
///
/// Gets the parameter of the curve.
///
///
/// The parameter of the curve.
///
protected abstract X9ECParameters CurveParameter { get; }
private ECDHCBasicAgreement _keyAgreement;
private ECDomainParameters _domainParameters;
///
/// Starts key exchange algorithm.
///
/// The session.
/// Key exchange init message.
public override void Start(Session session, KeyExchangeInitMessage message)
{
base.Start(session, message);
Session.RegisterMessage("SSH_MSG_KEX_ECDH_REPLY");
Session.KeyExchangeEcdhReplyMessageReceived += Session_KeyExchangeEcdhReplyMessageReceived;
_domainParameters = new ECDomainParameters(CurveParameter.Curve,
CurveParameter.G,
CurveParameter.N,
CurveParameter.H,
CurveParameter.GetSeed());
var g = new ECKeyPairGenerator();
g.Init(new ECKeyGenerationParameters(_domainParameters, new SecureRandom()));
var aKeyPair = g.GenerateKeyPair();
_keyAgreement = new ECDHCBasicAgreement();
_keyAgreement.Init(aKeyPair.Private);
_clientExchangeValue = ((ECPublicKeyParameters)aKeyPair.Public).Q.GetEncoded();
SendMessage(new KeyExchangeEcdhInitMessage(_clientExchangeValue));
}
///
/// Finishes key exchange algorithm.
///
public override void Finish()
{
base.Finish();
Session.KeyExchangeEcdhReplyMessageReceived -= Session_KeyExchangeEcdhReplyMessageReceived;
}
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 cordSize = (serverExchangeValue.Length - 1) / 2;
var x = new byte[cordSize];
Buffer.BlockCopy(serverExchangeValue, 1, x, 0, x.Length); // first byte is format. should be checked and passed to bouncy castle?
var y = new byte[cordSize];
Buffer.BlockCopy(serverExchangeValue, cordSize + 1, y, 0, y.Length);
var c = (FpCurve)_domainParameters.Curve;
var q = c.CreatePoint(new Org.BouncyCastle.Math.BigInteger(1, x), new Org.BouncyCastle.Math.BigInteger(1, y));
var publicKey = new ECPublicKeyParameters("ECDH", q, _domainParameters);
var k1 = _keyAgreement.CalculateAgreement(publicKey);
SharedKey = k1.ToByteArray().ToBigInteger2().ToByteArray().Reverse();
}
}
}