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

KeyExchangeSNtruP761X25519Sha512

using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Agreement; using Org.BouncyCastle.Crypto.Generators; 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; using System.Globalization; namespace Renci.SshNet.Security { internal sealed class KeyExchangeSNtruP761X25519Sha512 : KeyExchangeEC { private SNtruPrimeKemExtractor _sntrup761Extractor; private X25519Agreement _x25519Agreement; public override string Name => "sntrup761x25519-sha512"; protected override int HashSize => 512; 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; SNtruPrimeKeyPairGenerator sNtruPrimeKeyPairGenerator = new SNtruPrimeKeyPairGenerator(); sNtruPrimeKeyPairGenerator.Init(new SNtruPrimeKeyGenerationParameters(CryptoAbstraction.SecureRandom, SNtruPrimeParameters.sntrup761)); AsymmetricCipherKeyPair asymmetricCipherKeyPair = sNtruPrimeKeyPairGenerator.GenerateKeyPair(); _sntrup761Extractor = new SNtruPrimeKemExtractor((SNtruPrimePrivateKeyParameters)asymmetricCipherKeyPair.Private); X25519KeyPairGenerator x25519KeyPairGenerator = new X25519KeyPairGenerator(); x25519KeyPairGenerator.Init(new X25519KeyGenerationParameters(CryptoAbstraction.SecureRandom)); AsymmetricCipherKeyPair asymmetricCipherKeyPair2 = x25519KeyPairGenerator.GenerateKeyPair(); _x25519Agreement = new X25519Agreement(); _x25519Agreement.Init(asymmetricCipherKeyPair2.Private); byte[] encoded = ((SNtruPrimePublicKeyParameters)asymmetricCipherKeyPair.Public).GetEncoded(); byte[] encoded2 = ((X25519PublicKeyParameters)asymmetricCipherKeyPair2.Public).GetEncoded(); _clientExchangeValue = encoded.Concat(encoded2); SendMessage(new KeyExchangeEcdhInitMessage(_clientExchangeValue)); } public override void Finish() { base.Finish(); 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.EncapsulationLength + X25519PublicKeyParameters.KeySize) throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Bad Q_S length: {0}.", serverExchangeValue.Length), DisconnectReason.KeyExchangeFailed); byte[] encapsulation = serverExchangeValue.Take(_sntrup761Extractor.EncapsulationLength); byte[] array = _sntrup761Extractor.ExtractSecret(encapsulation); int num = array.Length; X25519PublicKeyParameters publicKey = new X25519PublicKeyParameters(serverExchangeValue, _sntrup761Extractor.EncapsulationLength); Array.Resize(ref array, num + _x25519Agreement.AgreementSize); _x25519Agreement.CalculateAgreement(publicKey, array, num); base.SharedKey = CryptoAbstraction.HashSHA512(array); } } }