Switch to X25519 + ML-KEM-768 encryption
All checks were successful
Server Build / publish (push) Successful in 29s
Voice Build / publish (push) Successful in 26s

This commit is contained in:
olcxja 2026-05-27 20:39:40 +02:00
commit 79af6fcddf
5 changed files with 62 additions and 85 deletions

View file

@ -6,6 +6,7 @@ using LarpixServer.Filesystem;
using LarpixServer.Utils; using LarpixServer.Utils;
using LarpixServer.Utils.Jsons; using LarpixServer.Utils.Jsons;
using static LarpixServer.Utils.Utils; using static LarpixServer.Utils.Utils;
using Org.BouncyCastle.Crypto.Parameters;
namespace LarpixServer.Account; namespace LarpixServer.Account;
@ -81,12 +82,10 @@ public class Requests
context.Response.ContentType = mimeTypes["json"]; context.Response.ContentType = mimeTypes["json"];
(BigInteger p, BigInteger g, BigInteger pubServer, BigInteger secretServer) serverInfo = var serverInfo = Encryption.Encryption.InitHybridKEM();
Encryption.Encryption.Init();
KeyExchangePayload payload = new KeyExchangePayload(); KeyExchangePayload payload = new KeyExchangePayload();
payload.p = serverInfo.p.ToString(); payload.pubX25519 = Convert.ToBase64String(serverInfo.pubX25519);
payload.g = serverInfo.g.ToString(); payload.pubMlKem = Convert.ToBase64String(serverInfo.pubMlKem);
payload.pubServer = serverInfo.pubServer.ToString();
payload.idKey = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + "\n"; payload.idKey = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + "\n";
while (createHolder.ContainsKey(payload.idKey)) while (createHolder.ContainsKey(payload.idKey))
@ -96,7 +95,8 @@ public class Requests
CreateHolder dataHolder = new(); CreateHolder dataHolder = new();
dataHolder.date = DateTimeOffset.UtcNow; dataHolder.date = DateTimeOffset.UtcNow;
dataHolder.serverInfo = serverInfo; dataHolder.privX25519Server = serverInfo.privX25519;
dataHolder.privMlKemServer = serverInfo.privMlKem;
createHolder.TryAdd(payload.idKey, dataHolder); createHolder.TryAdd(payload.idKey, dataHolder);
@ -123,9 +123,12 @@ public class Requests
} }
entry.date = DateTimeOffset.UtcNow; entry.date = DateTimeOffset.UtcNow;
entry.pubClient = BigInteger.Parse(serializedBody.pubClient);
byte[] sharedKey = Encryption.Encryption.CalcCommunicationKey(entry.pubClient, byte[] pubX25519Client = Convert.FromBase64String(serializedBody.pubX25519);
entry.serverInfo.secretServer, entry.serverInfo.p); byte[] ciphertextMlKem = Convert.FromBase64String(serializedBody.ciphertextMlKem);
byte[] sharedKey = Encryption.Encryption.CalcHybridSharedKey(
pubX25519Client, entry.privX25519Server, ciphertextMlKem, entry.privMlKemServer);
entry.name = Encryption.Encryption.Decrypt(serializedBody.username, sharedKey); entry.name = Encryption.Encryption.Decrypt(serializedBody.username, sharedKey);
@ -649,6 +652,6 @@ public class CreateHolder
public string name; public string name;
public string pass; public string pass;
public string captcha; public string captcha;
public (BigInteger p, BigInteger g, BigInteger pubServer, BigInteger secretServer) serverInfo; public X25519PrivateKeyParameters privX25519Server;
public BigInteger pubClient; public MLKemPrivateKeyParameters privMlKemServer;
} }

View file

@ -47,7 +47,7 @@ public class Utils
public static bool IsUserLocal(string usernameWD, out string domain) public static bool IsUserLocal(string usernameWD, out string domain)
{ {
if (usernameWD.EndsWith(":" + DOMAIN)) if (!usernameWD.Contains(':') || usernameWD.EndsWith(":" + DOMAIN))
{ {
domain = DOMAIN; domain = DOMAIN;
return true; return true;

View file

@ -2,87 +2,60 @@ using System.Globalization;
using System.Numerics; using System.Numerics;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Kems;
using Org.BouncyCastle.Security;
namespace LarpixServer.Encryption; namespace LarpixServer.Encryption;
public class Encryption public class Encryption
{ {
private static readonly string PrimeHex = public static (byte[] pubX25519, X25519PrivateKeyParameters privX25519, byte[] pubMlKem, MLKemPrivateKeyParameters privMlKem) InitHybridKEM()
"0" +
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" +
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" +
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" +
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" +
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" +
"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" +
"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" +
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" +
"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" +
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" +
"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" +
"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" +
"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" +
"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" +
"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" +
"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" +
"FFFFFFFFFFFFFFFF";
/// <summary>
/// RFC 3526 4096-bit MODP Group (ID: 16) Prime
/// </summary>
public static BigInteger Prime { get; } = BigInteger.Parse(PrimeHex, NumberStyles.AllowHexSpecifier);
public static void Chuj()
{ {
(BigInteger p, BigInteger g, BigInteger pubServer, BigInteger secretServer) serverInfo = Init(); var random = new SecureRandom();
(BigInteger pubClient, byte[] aesKey) clientInfo = CalcCommunicationKeyClient(serverInfo.p, serverInfo.g, serverInfo.pubServer);
byte[] sharedKey = CalcCommunicationKey(clientInfo.pubClient, serverInfo.secretServer, serverInfo.p); // X25519
var x25519KeyGen = new X25519KeyPairGenerator();
x25519KeyGen.Init(new X25519KeyGenerationParameters(random));
var x25519Kp = x25519KeyGen.GenerateKeyPair();
// ML-KEM-768
var mlkemGenParams = new MLKemKeyGenerationParameters(random, MLKemParameters.ml_kem_768);
var mlkemKeyGen = new MLKemKeyPairGenerator();
mlkemKeyGen.Init(mlkemGenParams);
var mlkemKp = mlkemKeyGen.GenerateKeyPair();
if (sharedKey == clientInfo.aesKey) return (
{ ((X25519PublicKeyParameters)x25519Kp.Public).GetEncoded(),
Console.WriteLine("key ok"); (X25519PrivateKeyParameters)x25519Kp.Private,
} ((MLKemPublicKeyParameters)mlkemKp.Public).GetEncoded(),
(MLKemPrivateKeyParameters)mlkemKp.Private
);
} }
public static (BigInteger p, BigInteger g, BigInteger pubServer, BigInteger secretServer) Init() public static byte[] CalcHybridSharedKey(byte[] pubX25519Client, X25519PrivateKeyParameters privX25519Server, byte[] ciphertextMlKem, MLKemPrivateKeyParameters privMlKemServer)
{ {
BigInteger p = Prime;//GenerateRandomBigInt(4096); // X25519 Agreement
BigInteger g = 2; var x25519Agreement = new Org.BouncyCastle.Crypto.Agreement.X25519Agreement();
BigInteger secretServer = GenerateRandomBigInt(4096); x25519Agreement.Init(privX25519Server);
var secretX25519 = new byte[32];
x25519Agreement.CalculateAgreement(new X25519PublicKeyParameters(pubX25519Client), secretX25519, 0);
BigInteger pubServer = BigInteger.ModPow(g, secretServer, p); // ML-KEM Decapsulation
var decapsulator = new MLKemDecapsulator(MLKemParameters.ml_kem_768);
decapsulator.Init(privMlKemServer);
var secretMlKem = new byte[decapsulator.SecretLength];
decapsulator.Decapsulate(ciphertextMlKem, secretMlKem);
return (p, g, pubServer, secretServer); // Combine and Hash: SHA256(X25519_Secret || MLKEM_Secret)
} var combined = new byte[secretX25519.Length + secretMlKem.Length];
Buffer.BlockCopy(secretX25519, 0, combined, 0, secretX25519.Length);
public static (BigInteger pubClient, byte[] aesKey) CalcCommunicationKeyClient(BigInteger p, BigInteger g, BigInteger pubServer) Buffer.BlockCopy(secretMlKem, 0, combined, secretX25519.Length, secretMlKem.Length);
{
BigInteger secretClient = GenerateRandomBigInt(4096);
BigInteger pubClient = BigInteger.ModPow(g, secretClient, p);
BigInteger sharedSecret = BigInteger.ModPow(pubServer, secretClient, p);
byte[] aesKey;
using (SHA256 sha256 = SHA256.Create()) using (SHA256 sha256 = SHA256.Create())
{ {
aesKey = sha256.ComputeHash(sharedSecret.ToByteArray(isUnsigned: true, isBigEndian: true)); return sha256.ComputeHash(combined);
}
return (pubClient, aesKey);
}
public static byte[] CalcCommunicationKey(BigInteger pubClient, BigInteger secretServer, BigInteger p)
{
BigInteger sharedSecret = BigInteger.ModPow(pubClient, secretServer, p);
using (SHA256 sha256 = SHA256.Create())
{
return sha256.ComputeHash(sharedSecret.ToByteArray(isUnsigned: true, isBigEndian: true));
} }
} }

View file

@ -8,6 +8,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" />
<PackageReference Include="SkiaSharp" Version="3.119.2" /> <PackageReference Include="SkiaSharp" Version="3.119.2" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="3.119.2" /> <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="3.119.2" />
</ItemGroup> </ItemGroup>

View file

@ -21,15 +21,15 @@ public class Universal3String
public class KeyExchangePayload public class KeyExchangePayload
{ {
public string p { get; set; } public string pubX25519 { get; set; }
public string g { get; set; } public string pubMlKem { get; set; }
public string pubServer { get; set; }
public string idKey { get; set; } public string idKey { get; set; }
} }
public class KeyExchangePayloadClient public class KeyExchangePayloadClient
{ {
public string pubClient { get; set; } public string pubX25519 { get; set; }
public string ciphertextMlKem { get; set; }
public string idKey { get; set; } public string idKey { get; set; }
public string username { get; set; } public string username { get; set; }
public string password { get; set; } public string password { get; set; }