<PackageReference Include="SSH.NET" Version="2025.1.0" />

KeyExchangeSNtruP761X25519Sha512

using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Pqc.Crypto.NtruPrime; using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; using System.Globalization; namespace Renci.SshNet.Security { internal sealed class KeyExchangeSNtruP761X25519Sha512 : KeyExchangeECCurve25519 { private SNtruPrimeKemExtractor _sntrup761Extractor; public override string Name => "sntrup761x25519-sha512"; protected override int HashSize => 512; protected override void StartImpl() { base.Session.RegisterMessage("SSH_MSG_KEX_ECDH_REPLY"); base.Session.KeyExchangeEcdhReplyMessageReceived += Session_KeyExchangeEcdhReplyMessageReceived; SNtruPrimeKeyPairGenerator val = new SNtruPrimeKeyPairGenerator(); val.Init(new SNtruPrimeKeyGenerationParameters(CryptoAbstraction.SecureRandom, SNtruPrimeParameters.sntrup761)); AsymmetricCipherKeyPair val2 = val.GenerateKeyPair(); _sntrup761Extractor = new SNtruPrimeKemExtractor(val2.get_Private()); byte[] encoded = val2.get_Public().GetEncoded(); byte[] second = _impl.GenerateClientPublicKey(); _clientExchangeValue = encoded.Concat(second); SendMessage(new KeyExchangeEcdhInitMessage(_clientExchangeValue)); } protected override void FinishImpl() { base.Session.KeyExchangeEcdhReplyMessageReceived -= Session_KeyExchangeEcdhReplyMessageReceived; } protected override byte[] Hash(byte[] hashData) { return CryptoAbstraction.HashSHA512(hashData); } 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; if (serverExchangeValue.Length != _sntrup761Extractor.get_EncapsulationLength() + X25519PublicKeyParameters.KeySize) throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Bad Q_S length: {0}.", serverExchangeValue.Length), DisconnectReason.KeyExchangeFailed); byte[] array = serverExchangeValue.Take(_sntrup761Extractor.get_EncapsulationLength()); byte[] first = _sntrup761Extractor.ExtractSecret(array); byte[] second = _impl.CalculateAgreement(serverExchangeValue.Take(_sntrup761Extractor.get_EncapsulationLength(), X25519PublicKeyParameters.KeySize)); base.SharedKey = CryptoAbstraction.HashSHA512(first.Concat(second)); } } }