KeyExchangeECDH
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Agreement;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math.EC;
using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Transport;
using System;
using System.Security.Cryptography;
namespace Renci.SshNet.Security
{
internal abstract class KeyExchangeECDH : KeyExchangeEC
{
private sealed class BclImpl : Impl
{
private readonly ECCurve _curve;
private readonly ECDiffieHellman _clientECDH;
public BclImpl(ECCurve curve)
{
_curve = curve;
_clientECDH = ECDiffieHellman.Create();
}
public unsafe override byte[] GenerateClientECPoint()
{
_clientECDH.GenerateKey(_curve);
return EncodeECPoint(((IntPtr)(void*)_clientECDH.get_PublicKey().ExportParameters()).Q);
}
public override byte[] CalculateAgreement(byte[] serverECPoint)
{
ECPoint q = DecodeECPoint(serverECPoint);
ECParameters val = default(ECParameters);
val.Curve = _curve;
val.Q = q;
ECDiffieHellman val2 = ECDiffieHellman.Create(val);
try {
return _clientECDH.DeriveRawSecretAgreement(val2.get_PublicKey());
} finally {
((IDisposable)val2)?.Dispose();
}
}
private unsafe static byte[] EncodeECPoint(ECPoint point)
{
byte[] array = new byte[1 + ((IntPtr)(void*)point).X.Length + ((IntPtr)(void*)point).Y.Length];
array[0] = 4;
Buffer.BlockCopy(((IntPtr)(void*)point).X, 0, array, 1, ((IntPtr)(void*)point).X.Length);
Buffer.BlockCopy(((IntPtr)(void*)point).Y, 0, array, ((IntPtr)(void*)point).X.Length + 1, ((IntPtr)(void*)point).Y.Length);
return array;
}
private static ECPoint DecodeECPoint(byte[] q)
{
int num = (q.Length - 1) / 2;
byte[] array = new byte[num];
byte[] array2 = new byte[num];
Buffer.BlockCopy(q, 1, array, 0, array.Length);
Buffer.BlockCopy(q, num + 1, array2, 0, array2.Length);
ECPoint result = default(ECPoint);
result.X = array;
result.Y = array2;
return result;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
_clientECDH.Dispose();
}
}
private sealed class BouncyCastleImpl : Impl
{
private readonly ECDomainParameters _domainParameters;
private readonly ECDHCBasicAgreement _keyAgreement;
public BouncyCastleImpl(X9ECParameters curveParameters)
{
_domainParameters = new ECDomainParameters(curveParameters);
_keyAgreement = new ECDHCBasicAgreement();
}
public override byte[] GenerateClientECPoint()
{
ECKeyPairGenerator val = new ECKeyPairGenerator();
val.Init(new ECKeyGenerationParameters(_domainParameters, CryptoAbstraction.SecureRandom));
AsymmetricCipherKeyPair val2 = val.GenerateKeyPair();
_keyAgreement.Init(val2.get_Private());
return val2.get_Public().get_Q().GetEncoded();
}
public override byte[] CalculateAgreement(byte[] serverECPoint)
{
ECPoint val = _domainParameters.get_Curve().DecodePoint(serverECPoint);
ECPublicKeyParameters val2 = new ECPublicKeyParameters("ECDH", val, _domainParameters);
return _keyAgreement.CalculateAgreement(val2).ToByteArray();
}
}
private abstract class Impl : IDisposable
{
public abstract byte[] GenerateClientECPoint();
public abstract byte[] CalculateAgreement(byte[] serverECPoint);
protected virtual void Dispose(bool disposing)
{
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
private Impl _impl;
protected abstract ECCurve Curve { get; }
protected abstract X9ECParameters CurveParameter { get; }
public override void Start(Session session, KeyExchangeInitMessage message, bool sendClientInitMessage)
{
base.Start(session, message, sendClientInitMessage);
base.Session.RegisterMessage("SSH_MSG_KEX_ECDH_REPLY");
base.Session.KeyExchangeEcdhReplyMessageReceived += Session_KeyExchangeEcdhReplyMessageReceived;
if (!OperatingSystem.IsWindows() || OperatingSystem.IsWindowsVersionAtLeast(10, 0, 0, 0))
_impl = new BclImpl(Curve);
else
_impl = new BouncyCastleImpl(CurveParameter);
_clientExchangeValue = _impl.GenerateClientECPoint();
SendMessage(new KeyExchangeEcdhInitMessage(_clientExchangeValue));
}
public override void Finish()
{
base.Finish();
base.Session.KeyExchangeEcdhReplyMessageReceived -= Session_KeyExchangeEcdhReplyMessageReceived;
}
private void Session_KeyExchangeEcdhReplyMessageReceived(object sender, MessageEventArgs<KeyExchangeEcdhReplyMessage> e)
{
KeyExchangeEcdhReplyMessage message = e.Message;
base.Session.UnRegisterMessage("SSH_MSG_KEX_ECDH_REPLY");
HandleServerEcdhReply(message.KS, message.QS, message.Signature);
Finish();
}
private void HandleServerEcdhReply(byte[] hostKey, byte[] serverExchangeValue, byte[] signature)
{
_serverExchangeValue = serverExchangeValue;
_hostKey = hostKey;
_signature = signature;
byte[] data = _impl.CalculateAgreement(serverExchangeValue);
base.SharedKey = data.ToBigInteger2().ToByteArray(false, true);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
_impl?.Dispose();
}
}
}