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.
}