|  | 
|  | 1 | +using System; | 
|  | 2 | +using System.Globalization; | 
|  | 3 | +using System.Linq; | 
|  | 4 | + | 
|  | 5 | +using Org.BouncyCastle.Crypto.Agreement; | 
|  | 6 | +using Org.BouncyCastle.Crypto.Generators; | 
|  | 7 | +using Org.BouncyCastle.Crypto.Parameters; | 
|  | 8 | +using Org.BouncyCastle.Pqc.Crypto.NtruPrime; | 
|  | 9 | + | 
|  | 10 | +using Renci.SshNet.Abstractions; | 
|  | 11 | +using Renci.SshNet.Common; | 
|  | 12 | +using Renci.SshNet.Messages.Transport; | 
|  | 13 | + | 
|  | 14 | +namespace Renci.SshNet.Security | 
|  | 15 | +{ | 
|  | 16 | +    internal sealed class KeyExchangeSNtruP761X25519Sha512 : KeyExchangeEC | 
|  | 17 | +    { | 
|  | 18 | +        private SNtruPrimeKemExtractor _sntrup761Extractor; | 
|  | 19 | +        private X25519Agreement _x25519Agreement; | 
|  | 20 | + | 
|  | 21 | +        /// <summary> | 
|  | 22 | +        /// Gets algorithm name. | 
|  | 23 | +        /// </summary> | 
|  | 24 | +        public override string Name | 
|  | 25 | +        { | 
|  | 26 | +            get { return "sntrup761x25519-sha512"; } | 
|  | 27 | +        } | 
|  | 28 | + | 
|  | 29 | +        /// <summary> | 
|  | 30 | +        /// Gets the size, in bits, of the computed hash code. | 
|  | 31 | +        /// </summary> | 
|  | 32 | +        /// <value> | 
|  | 33 | +        /// The size, in bits, of the computed hash code. | 
|  | 34 | +        /// </value> | 
|  | 35 | +        protected override int HashSize | 
|  | 36 | +        { | 
|  | 37 | +            get { return 512; } | 
|  | 38 | +        } | 
|  | 39 | + | 
|  | 40 | +        /// <inheritdoc/> | 
|  | 41 | +        public override void Start(Session session, KeyExchangeInitMessage message, bool sendClientInitMessage) | 
|  | 42 | +        { | 
|  | 43 | +            base.Start(session, message, sendClientInitMessage); | 
|  | 44 | + | 
|  | 45 | +            Session.RegisterMessage("SSH_MSG_KEX_ECDH_REPLY"); | 
|  | 46 | + | 
|  | 47 | +            Session.KeyExchangeEcdhReplyMessageReceived += Session_KeyExchangeEcdhReplyMessageReceived; | 
|  | 48 | + | 
|  | 49 | +            var sntrup761KeyPairGenerator = new SNtruPrimeKeyPairGenerator(); | 
|  | 50 | +            sntrup761KeyPairGenerator.Init(new SNtruPrimeKeyGenerationParameters(CryptoAbstraction.SecureRandom, SNtruPrimeParameters.sntrup761)); | 
|  | 51 | +            var sntrup761KeyPair = sntrup761KeyPairGenerator.GenerateKeyPair(); | 
|  | 52 | + | 
|  | 53 | +            _sntrup761Extractor = new SNtruPrimeKemExtractor((SNtruPrimePrivateKeyParameters)sntrup761KeyPair.Private); | 
|  | 54 | + | 
|  | 55 | +            var x25519KeyPairGenerator = new X25519KeyPairGenerator(); | 
|  | 56 | +            x25519KeyPairGenerator.Init(new X25519KeyGenerationParameters(CryptoAbstraction.SecureRandom)); | 
|  | 57 | +            var x25519KeyPair = x25519KeyPairGenerator.GenerateKeyPair(); | 
|  | 58 | + | 
|  | 59 | +            _x25519Agreement = new X25519Agreement(); | 
|  | 60 | +            _x25519Agreement.Init(x25519KeyPair.Private); | 
|  | 61 | + | 
|  | 62 | +            var sntrup761PublicKey = ((SNtruPrimePublicKeyParameters)sntrup761KeyPair.Public).GetEncoded(); | 
|  | 63 | +            var x25519PublicKey = ((X25519PublicKeyParameters)x25519KeyPair.Public).GetEncoded(); | 
|  | 64 | + | 
|  | 65 | +            _clientExchangeValue = sntrup761PublicKey.Concat(x25519PublicKey); | 
|  | 66 | + | 
|  | 67 | +            SendMessage(new KeyExchangeEcdhInitMessage(_clientExchangeValue)); | 
|  | 68 | +        } | 
|  | 69 | + | 
|  | 70 | +        /// <summary> | 
|  | 71 | +        /// Finishes key exchange algorithm. | 
|  | 72 | +        /// </summary> | 
|  | 73 | +        public override void Finish() | 
|  | 74 | +        { | 
|  | 75 | +            base.Finish(); | 
|  | 76 | + | 
|  | 77 | +            Session.KeyExchangeEcdhReplyMessageReceived -= Session_KeyExchangeEcdhReplyMessageReceived; | 
|  | 78 | +        } | 
|  | 79 | + | 
|  | 80 | +        /// <summary> | 
|  | 81 | +        /// Hashes the specified data bytes. | 
|  | 82 | +        /// </summary> | 
|  | 83 | +        /// <param name="hashData">The hash data.</param> | 
|  | 84 | +        /// <returns> | 
|  | 85 | +        /// The hash of the data. | 
|  | 86 | +        /// </returns> | 
|  | 87 | +        protected override byte[] Hash(byte[] hashData) | 
|  | 88 | +        { | 
|  | 89 | +            return CryptoAbstraction.HashSHA512(hashData); | 
|  | 90 | +        } | 
|  | 91 | + | 
|  | 92 | +        private void Session_KeyExchangeEcdhReplyMessageReceived(object sender, MessageEventArgs<KeyExchangeEcdhReplyMessage> e) | 
|  | 93 | +        { | 
|  | 94 | +            var message = e.Message; | 
|  | 95 | + | 
|  | 96 | +            // Unregister message once received | 
|  | 97 | +            Session.UnRegisterMessage("SSH_MSG_KEX_ECDH_REPLY"); | 
|  | 98 | + | 
|  | 99 | +            HandleServerEcdhReply(message.KS, message.QS, message.Signature); | 
|  | 100 | + | 
|  | 101 | +            // When SSH_MSG_KEX_ECDH_REPLY received key exchange is completed | 
|  | 102 | +            Finish(); | 
|  | 103 | +        } | 
|  | 104 | + | 
|  | 105 | +        /// <summary> | 
|  | 106 | +        /// Handles the server DH reply message. | 
|  | 107 | +        /// </summary> | 
|  | 108 | +        /// <param name="hostKey">The host key.</param> | 
|  | 109 | +        /// <param name="serverExchangeValue">The server exchange value.</param> | 
|  | 110 | +        /// <param name="signature">The signature.</param> | 
|  | 111 | +        private void HandleServerEcdhReply(byte[] hostKey, byte[] serverExchangeValue, byte[] signature) | 
|  | 112 | +        { | 
|  | 113 | +            _serverExchangeValue = serverExchangeValue; | 
|  | 114 | +            _hostKey = hostKey; | 
|  | 115 | +            _signature = signature; | 
|  | 116 | + | 
|  | 117 | +            if (serverExchangeValue.Length != _sntrup761Extractor.EncapsulationLength + X25519PublicKeyParameters.KeySize) | 
|  | 118 | +            { | 
|  | 119 | +                throw new SshConnectionException( | 
|  | 120 | +                    string.Format(CultureInfo.CurrentCulture, "Bad Q_S length: {0}.", serverExchangeValue.Length), | 
|  | 121 | +                    DisconnectReason.KeyExchangeFailed); | 
|  | 122 | +            } | 
|  | 123 | + | 
|  | 124 | +            var sntrup761CipherText = serverExchangeValue.Take(_sntrup761Extractor.EncapsulationLength); | 
|  | 125 | +            var secret = _sntrup761Extractor.ExtractSecret(sntrup761CipherText); | 
|  | 126 | +            var sntrup761SecretLength = secret.Length; | 
|  | 127 | + | 
|  | 128 | +            var x25519PublicKey = new X25519PublicKeyParameters(serverExchangeValue, _sntrup761Extractor.EncapsulationLength); | 
|  | 129 | +            Array.Resize(ref secret, sntrup761SecretLength + _x25519Agreement.AgreementSize); | 
|  | 130 | +            _x25519Agreement.CalculateAgreement(x25519PublicKey, secret, sntrup761SecretLength); | 
|  | 131 | + | 
|  | 132 | +            SharedKey = CryptoAbstraction.HashSHA512(secret); | 
|  | 133 | +        } | 
|  | 134 | +    } | 
|  | 135 | +} | 
0 commit comments