diff --git a/Directory.Packages.props b/Directory.Packages.props index a4d12ed86..b504856fd 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -12,6 +12,7 @@ + diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj index 446865229..45b2087ca 100644 --- a/src/Renci.SshNet/Renci.SshNet.csproj +++ b/src/Renci.SshNet/Renci.SshNet.csproj @@ -49,10 +49,11 @@ - - + + + True diff --git a/src/Renci.SshNet/Security/KeyExchangeMLKem768X25519Sha256.cs b/src/Renci.SshNet/Security/KeyExchangeMLKem768X25519Sha256.cs index 469a53738..491cb9843 100644 --- a/src/Renci.SshNet/Security/KeyExchangeMLKem768X25519Sha256.cs +++ b/src/Renci.SshNet/Security/KeyExchangeMLKem768X25519Sha256.cs @@ -1,5 +1,6 @@ -using System.Globalization; -using System.Linq; +using System; +using System.Globalization; +using System.Security.Cryptography; using Org.BouncyCastle.Crypto.Agreement; using Org.BouncyCastle.Crypto.Generators; @@ -12,8 +13,11 @@ namespace Renci.SshNet.Security { +#pragma warning disable SYSLIB5006 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. internal sealed class KeyExchangeMLKem768X25519Sha256 : KeyExchangeEC { + private readonly MLKemAlgorithm _mlkem768 = MLKemAlgorithm.MLKem768; + private MLKem _mlkem; private MLKemDecapsulator _mlkemDecapsulator; private X25519Agreement _x25519Agreement; @@ -45,12 +49,22 @@ public override void Start(Session session, KeyExchangeInitMessage message, bool Session.KeyExchangeHybridReplyMessageReceived += Session_KeyExchangeHybridReplyMessageReceived; - var mlkem768KeyPairGenerator = new MLKemKeyPairGenerator(); - mlkem768KeyPairGenerator.Init(new MLKemKeyGenerationParameters(CryptoAbstraction.SecureRandom, MLKemParameters.ml_kem_768)); - var mlkem768KeyPair = mlkem768KeyPairGenerator.GenerateKeyPair(); + if (MLKem.IsSupported) + { + _mlkem = MLKem.GenerateKey(_mlkem768); + _clientExchangeValue = _mlkem.ExportEncapsulationKey(); + } + else + { + var mlkem768KeyPairGenerator = new MLKemKeyPairGenerator(); + mlkem768KeyPairGenerator.Init(new MLKemKeyGenerationParameters(CryptoAbstraction.SecureRandom, MLKemParameters.ml_kem_768)); + var mlkem768KeyPair = mlkem768KeyPairGenerator.GenerateKeyPair(); + + _mlkemDecapsulator = new MLKemDecapsulator(MLKemParameters.ml_kem_768); + _mlkemDecapsulator.Init(mlkem768KeyPair.Private); - _mlkemDecapsulator = new MLKemDecapsulator(MLKemParameters.ml_kem_768); - _mlkemDecapsulator.Init(mlkem768KeyPair.Private); + _clientExchangeValue = ((MLKemPublicKeyParameters)mlkem768KeyPair.Public).GetEncoded(); + } var x25519KeyPairGenerator = new X25519KeyPairGenerator(); x25519KeyPairGenerator.Init(new X25519KeyGenerationParameters(CryptoAbstraction.SecureRandom)); @@ -59,10 +73,9 @@ public override void Start(Session session, KeyExchangeInitMessage message, bool _x25519Agreement = new X25519Agreement(); _x25519Agreement.Init(x25519KeyPair.Private); - var mlkem768PublicKey = ((MLKemPublicKeyParameters)mlkem768KeyPair.Public).GetEncoded(); - var x25519PublicKey = ((X25519PublicKeyParameters)x25519KeyPair.Public).GetEncoded(); + Array.Resize(ref _clientExchangeValue, _mlkem768.EncapsulationKeySizeInBytes + X25519PublicKeyParameters.KeySize); - _clientExchangeValue = mlkem768PublicKey.Concat(x25519PublicKey); + ((X25519PublicKeyParameters)x25519KeyPair.Public).Encode(_clientExchangeValue, _mlkem768.EncapsulationKeySizeInBytes); SendMessage(new KeyExchangeHybridInitMessage(_clientExchangeValue)); } @@ -114,21 +127,39 @@ private void HandleServerHybridReply(byte[] hostKey, byte[] serverExchangeValue, _hostKey = hostKey; _signature = signature; - if (serverExchangeValue.Length != _mlkemDecapsulator.EncapsulationLength + _x25519Agreement.AgreementSize) + if (serverExchangeValue.Length != _mlkem768.CiphertextSizeInBytes + _x25519Agreement.AgreementSize) { throw new SshConnectionException( string.Format(CultureInfo.CurrentCulture, "Bad S_Reply length: {0}.", serverExchangeValue.Length), DisconnectReason.KeyExchangeFailed); } - var secret = new byte[_mlkemDecapsulator.SecretLength + _x25519Agreement.AgreementSize]; + var secret = new byte[_mlkem768.SharedSecretSizeInBytes + _x25519Agreement.AgreementSize]; - _mlkemDecapsulator.Decapsulate(serverExchangeValue, 0, _mlkemDecapsulator.EncapsulationLength, secret, 0, _mlkemDecapsulator.SecretLength); + if (MLKem.IsSupported) + { + _mlkem.Decapsulate(serverExchangeValue.AsSpan(0, _mlkem768.CiphertextSizeInBytes), secret.AsSpan(0, _mlkem768.SharedSecretSizeInBytes)); + } + else + { + _mlkemDecapsulator.Decapsulate(serverExchangeValue, 0, _mlkemDecapsulator.EncapsulationLength, secret, 0, _mlkemDecapsulator.SecretLength); + } - var x25519PublicKey = new X25519PublicKeyParameters(serverExchangeValue, _mlkemDecapsulator.EncapsulationLength); - _x25519Agreement.CalculateAgreement(x25519PublicKey, secret, _mlkemDecapsulator.SecretLength); + var x25519PublicKey = new X25519PublicKeyParameters(serverExchangeValue, _mlkem768.CiphertextSizeInBytes); + _x25519Agreement.CalculateAgreement(x25519PublicKey, secret, _mlkem768.SharedSecretSizeInBytes); SharedKey = CryptoAbstraction.HashSHA256(secret); } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _mlkem?.Dispose(); + } + + base.Dispose(disposing); + } } +#pragma warning restore SYSLIB5006 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. }