mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-05 18:30:48 +02:00
Merge pull request #10191 from dgarske/csharp_pqc
C# Wrapper: ML-KEM and ML-DSA (Dilithium) Support
This commit is contained in:
@@ -87,6 +87,17 @@
|
||||
#define ECC_TIMING_RESISTANT
|
||||
#define HAVE_COMP_KEY
|
||||
|
||||
/* Enable ML-KEM, ML-DSA */
|
||||
#define HAVE_MLKEM
|
||||
#define WOLFSSL_WC_MLKEM
|
||||
#define WOLFSSL_HAVE_MLKEM
|
||||
/* Required for PQC with DTLS 1.3 (auto-enabled in settings.h, explicit for clarity) */
|
||||
#define WOLFSSL_DTLS_CH_FRAG
|
||||
#define HAVE_DILITHIUM
|
||||
#define WOLFSSL_WC_DILITHIUM
|
||||
#define WOLFSSL_SHAKE128
|
||||
#define WOLFSSL_SHAKE256
|
||||
|
||||
/* Disable features */
|
||||
#define NO_PSK
|
||||
|
||||
|
||||
@@ -674,6 +674,311 @@ public class wolfCrypt_Test_CSharp
|
||||
if (publicKeyB != IntPtr.Zero) wolfcrypt.Curve25519FreeKey(publicKeyB);
|
||||
} /* END curve25519_test */
|
||||
|
||||
private static void mlkem_test(wolfcrypt.MlKemTypes type)
|
||||
{
|
||||
int ret = 0;
|
||||
IntPtr keyA = IntPtr.Zero;
|
||||
IntPtr keyB = IntPtr.Zero;
|
||||
IntPtr heap = IntPtr.Zero;
|
||||
int devId = wolfcrypt.INVALID_DEVID;
|
||||
byte[] pubA = null;
|
||||
byte[] privA = null;
|
||||
byte[] cipherText = null;
|
||||
byte[] sharedSecretA = null;
|
||||
byte[] sharedSecretB = null;
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine("\nStarting " + type + " shared secret test ...");
|
||||
|
||||
/* Generate Key Pair */
|
||||
Console.WriteLine("Testing ML-KEM Key Generation...");
|
||||
|
||||
Console.WriteLine("Generate Key Pair A...");
|
||||
keyA = wolfcrypt.MlKemMakeKey(type, heap, devId);
|
||||
if (keyA == IntPtr.Zero)
|
||||
{
|
||||
ret = -1;
|
||||
Console.Error.WriteLine("Failed to generate key pair A.");
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("Initialize Key B for decode...");
|
||||
keyB = wolfcrypt.MlKemNew(type, heap, devId);
|
||||
if (keyB == IntPtr.Zero)
|
||||
{
|
||||
ret = -1;
|
||||
Console.Error.WriteLine("Failed to initialize key B for decode.");
|
||||
}
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("ML-KEM Key Generation test passed.");
|
||||
}
|
||||
|
||||
/* Encode */
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("Testing ML-KEM Key Encode...");
|
||||
ret = wolfcrypt.MlKemEncodePublicKey(keyA, out pubA);
|
||||
if (ret != 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to encode public key of A. Error code: {ret}");
|
||||
}
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
ret = wolfcrypt.MlKemEncodePrivateKey(keyA, out privA);
|
||||
if (ret != 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to encode private key of A. Error code: {ret}");
|
||||
}
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("ML-KEM Key Encode test passed.");
|
||||
}
|
||||
|
||||
/* Encapsulate */
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("Testing ML-KEM Encapsulation...");
|
||||
ret = wolfcrypt.MlKemEncapsulate(keyA, out cipherText, out sharedSecretA);
|
||||
if (ret != 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to encapsulate. Error code: {ret}");
|
||||
}
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("ML-KEM Encapsulation test passed.");
|
||||
}
|
||||
|
||||
/* Decode */
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("Testing ML-KEM Decode...");
|
||||
ret = wolfcrypt.MlKemDecodePrivateKey(keyB, privA);
|
||||
if (ret != 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to decode private key of A. Error code: {ret}");
|
||||
}
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
ret = wolfcrypt.MlKemDecodePublicKey(keyB, pubA);
|
||||
if (ret != 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to decode public key of A. Error code: {ret}");
|
||||
}
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("ML-KEM Decode test passed.");
|
||||
}
|
||||
|
||||
/* Decapsulate */
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("Testing ML-KEM Decapsulation...");
|
||||
ret = wolfcrypt.MlKemDecapsulate(keyB, cipherText, out sharedSecretB);
|
||||
if (ret != 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to decapsulate. Error code: {ret}");
|
||||
}
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("ML-KEM Decapsulation test passed.");
|
||||
}
|
||||
|
||||
/* Check */
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("Comparing Shared Secrets...");
|
||||
if (!wolfcrypt.ByteArrayVerify(sharedSecretA, sharedSecretB))
|
||||
{
|
||||
ret = -1;
|
||||
Console.Error.WriteLine($"Shared secrets do not match. Error code: {ret}");
|
||||
}
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("ML-KEM shared secret match.");
|
||||
}
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
throw new Exception("ML-KEM test failed.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"ML-KEM test failed: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
/* Cleanup */
|
||||
if (keyA != IntPtr.Zero)
|
||||
{
|
||||
ret = wolfcrypt.MlKemFreeKey(ref keyA);
|
||||
if (ret != 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to free MlKem key A. Error code: {ret}");
|
||||
}
|
||||
}
|
||||
if (keyB != IntPtr.Zero)
|
||||
{
|
||||
ret = wolfcrypt.MlKemFreeKey(ref keyB);
|
||||
if (ret != 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to free MlKem key B. Error code: {ret}");
|
||||
}
|
||||
}
|
||||
}
|
||||
} /* END mlkem_test */
|
||||
|
||||
private static void mldsa_test(wolfcrypt.MlDsaLevels level)
|
||||
{
|
||||
int ret = 0;
|
||||
IntPtr key = IntPtr.Zero;
|
||||
IntPtr importKey = IntPtr.Zero;
|
||||
IntPtr heap = IntPtr.Zero;
|
||||
int devId = wolfcrypt.INVALID_DEVID;
|
||||
byte[] privateKey = null;
|
||||
byte[] publicKey = null;
|
||||
byte[] message = Encoding.UTF8.GetBytes("This is some data to sign with ML-DSA");
|
||||
byte[] signature = null;
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine("\nStarting " + level + " key generation and signature test ...");
|
||||
|
||||
/* Generate Key */
|
||||
Console.WriteLine("Testing ML-DSA Key Generation...");
|
||||
key = wolfcrypt.MlDsaMakeKey(heap, devId, level);
|
||||
if (key == IntPtr.Zero)
|
||||
{
|
||||
ret = -1;
|
||||
Console.Error.WriteLine("Failed to generate keypair.");
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("ML-DSA Key Generation test passed.");
|
||||
}
|
||||
|
||||
/* Export */
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("Testing ML-DSA Key Export...");
|
||||
ret = wolfcrypt.MlDsaExportPrivateKey(key, out privateKey);
|
||||
if (ret != 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to export private key. Error code: {ret}");
|
||||
}
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
ret = wolfcrypt.MlDsaExportPublicKey(key, out publicKey);
|
||||
if (ret != 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to export public key. Error code: {ret}");
|
||||
}
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("ML-DSA Key Export test passed.");
|
||||
}
|
||||
|
||||
/* Import into a fresh key to test the full import workflow */
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("Testing ML-DSA Key Import...");
|
||||
/* Free the keygen key and create a fresh one for import */
|
||||
wolfcrypt.MlDsaFreeKey(ref key);
|
||||
importKey = wolfcrypt.MlDsaNew(heap, devId, level);
|
||||
if (importKey == IntPtr.Zero)
|
||||
{
|
||||
ret = -1;
|
||||
Console.Error.WriteLine("Failed to allocate key for import.");
|
||||
}
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
ret = wolfcrypt.MlDsaImportPrivateKey(privateKey, importKey);
|
||||
if (ret != 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to import private key. Error code: {ret}");
|
||||
}
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
ret = wolfcrypt.MlDsaImportPublicKey(publicKey, importKey);
|
||||
if (ret != 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to import public key. Error code: {ret}");
|
||||
}
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("ML-DSA Key Import test passed.");
|
||||
}
|
||||
|
||||
/* Sign with imported key */
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("Testing ML-DSA Signature Creation...");
|
||||
ret = wolfcrypt.MlDsaSignMsg(importKey, message, out signature);
|
||||
if (ret != 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to sign. Error code: {ret}");
|
||||
}
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine($"ML-DSA Signature Creation test passed. Signature Length: {signature.Length}");
|
||||
}
|
||||
|
||||
/* Verify with imported key */
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("Testing ML-DSA Signature Verification...");
|
||||
ret = wolfcrypt.MlDsaVerifyMsg(importKey, message, signature);
|
||||
if (ret != 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to verify message. Error code: {ret}");
|
||||
}
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
Console.WriteLine("ML-DSA Signature Verification test passed.");
|
||||
}
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
throw new Exception("ML-DSA test failed.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"ML-DSA test failed: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (key != IntPtr.Zero)
|
||||
{
|
||||
wolfcrypt.MlDsaFreeKey(ref key);
|
||||
}
|
||||
if (importKey != IntPtr.Zero)
|
||||
{
|
||||
wolfcrypt.MlDsaFreeKey(ref importKey);
|
||||
}
|
||||
}
|
||||
|
||||
} /* END mldsa_test */
|
||||
|
||||
private static void aes_gcm_test()
|
||||
{
|
||||
IntPtr aes = IntPtr.Zero;
|
||||
@@ -1126,6 +1431,18 @@ public class wolfCrypt_Test_CSharp
|
||||
|
||||
curve25519_test(); /* curve25519 shared secret test */
|
||||
|
||||
Console.WriteLine("\nStarting ML-KEM test");
|
||||
|
||||
mlkem_test(wolfcrypt.MlKemTypes.ML_KEM_512); /* ML-KEM test */
|
||||
mlkem_test(wolfcrypt.MlKemTypes.ML_KEM_768); /* ML-KEM test */
|
||||
mlkem_test(wolfcrypt.MlKemTypes.ML_KEM_1024); /* ML-KEM test */
|
||||
|
||||
Console.WriteLine("\nStarting ML-DSA test");
|
||||
|
||||
mldsa_test(wolfcrypt.MlDsaLevels.ML_DSA_44); /* ML-DSA test */
|
||||
mldsa_test(wolfcrypt.MlDsaLevels.ML_DSA_65); /* ML-DSA test */
|
||||
mldsa_test(wolfcrypt.MlDsaLevels.ML_DSA_87); /* ML-DSA test */
|
||||
|
||||
Console.WriteLine("\nStarting AES-GCM test");
|
||||
|
||||
aes_gcm_test(); /* AES_GCM test */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -509,6 +509,8 @@ namespace wolfSSL.CSharp
|
||||
private extern static int wolfSSL_shutdown(IntPtr ssl);
|
||||
[DllImport(wolfssl_dll)]
|
||||
private extern static void wolfSSL_free(IntPtr ssl);
|
||||
[DllImport(wolfssl_dll)]
|
||||
private static extern int wolfSSL_UseKeyShare(IntPtr ssl, ushort group);
|
||||
#else
|
||||
[DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private extern static IntPtr wolfSSL_new(IntPtr ctx);
|
||||
@@ -524,6 +526,8 @@ namespace wolfSSL.CSharp
|
||||
private extern static int wolfSSL_shutdown(IntPtr ssl);
|
||||
[DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private extern static void wolfSSL_free(IntPtr ssl);
|
||||
[DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int wolfSSL_UseKeyShare(IntPtr ssl, ushort group);
|
||||
#endif
|
||||
|
||||
/********************************
|
||||
@@ -709,6 +713,90 @@ namespace wolfSSL.CSharp
|
||||
public static readonly int WOLFSSL_LOAD_FLAG_IGNORE_ZEROFILE = 0x00000010;
|
||||
public static readonly int WOLFSSL_LOAD_VERIFY_DEFAULT_FLAGS = WOLFSSL_LOAD_FLAG_NONE;
|
||||
|
||||
/* from ssl.h */
|
||||
public enum NamedGroup
|
||||
{
|
||||
WOLFSSL_NAMED_GROUP_INVALID = 0,
|
||||
|
||||
WOLFSSL_ECC_SECP160K1 = 15,
|
||||
WOLFSSL_ECC_SECP160R1 = 16,
|
||||
WOLFSSL_ECC_SECP160R2 = 17,
|
||||
WOLFSSL_ECC_SECP192K1 = 18,
|
||||
WOLFSSL_ECC_SECP192R1 = 19,
|
||||
WOLFSSL_ECC_SECP224K1 = 20,
|
||||
WOLFSSL_ECC_SECP224R1 = 21,
|
||||
WOLFSSL_ECC_SECP256K1 = 22,
|
||||
WOLFSSL_ECC_SECP256R1 = 23,
|
||||
WOLFSSL_ECC_SECP384R1 = 24,
|
||||
WOLFSSL_ECC_SECP521R1 = 25,
|
||||
WOLFSSL_ECC_BRAINPOOLP256R1 = 26,
|
||||
WOLFSSL_ECC_BRAINPOOLP384R1 = 27,
|
||||
WOLFSSL_ECC_BRAINPOOLP512R1 = 28,
|
||||
WOLFSSL_ECC_X25519 = 29,
|
||||
WOLFSSL_ECC_X448 = 30,
|
||||
WOLFSSL_ECC_BRAINPOOLP256R1TLS13 = 31,
|
||||
WOLFSSL_ECC_BRAINPOOLP384R1TLS13 = 32,
|
||||
WOLFSSL_ECC_BRAINPOOLP512R1TLS13 = 33,
|
||||
WOLFSSL_ECC_SM2P256V1 = 41,
|
||||
WOLFSSL_ECC_MAX = 41,
|
||||
WOLFSSL_ECC_MAX_AVAIL = 46,
|
||||
/* Update use of disabled curves when adding value greater than 46. */
|
||||
|
||||
WOLFSSL_FFDHE_START = 256,
|
||||
WOLFSSL_FFDHE_2048 = 256,
|
||||
WOLFSSL_FFDHE_3072 = 257,
|
||||
WOLFSSL_FFDHE_4096 = 258,
|
||||
WOLFSSL_FFDHE_6144 = 259,
|
||||
WOLFSSL_FFDHE_8192 = 260,
|
||||
WOLFSSL_FFDHE_END = 511,
|
||||
|
||||
/* Old code points to keep compatibility with MlKem Round 3.
|
||||
* Taken from OQS's openssl provider, see:
|
||||
* https://github.com/open-quantum-safe/oqs-provider/blob/main/oqs-template/
|
||||
* oqs-kem-info.md
|
||||
*/
|
||||
WOLFSSL_KYBER_LEVEL1 = 570, /* KYBER_512 */
|
||||
WOLFSSL_KYBER_LEVEL3 = 572, /* KYBER_768 */
|
||||
WOLFSSL_KYBER_LEVEL5 = 573, /* KYBER_1024 */
|
||||
|
||||
WOLFSSL_P256_KYBER_LEVEL1 = 12090,
|
||||
WOLFSSL_P384_KYBER_LEVEL3 = 12092,
|
||||
WOLFSSL_P521_KYBER_LEVEL5 = 12093,
|
||||
WOLFSSL_X25519_KYBER_LEVEL1 = 12089,
|
||||
WOLFSSL_X448_KYBER_LEVEL3 = 12176,
|
||||
WOLFSSL_X25519_KYBER_LEVEL3 = 25497,
|
||||
WOLFSSL_P256_KYBER_LEVEL3 = 25498,
|
||||
|
||||
/* Taken from draft-ietf-tls-mlkem, see:
|
||||
* https://datatracker.ietf.org/doc/draft-ietf-tls-mlkem/
|
||||
*/
|
||||
WOLFSSL_ML_KEM_512 = 512,
|
||||
WOLFSSL_ML_KEM_768 = 513,
|
||||
WOLFSSL_ML_KEM_1024 = 514,
|
||||
|
||||
/* Taken from draft-ietf-tls-ecdhe-mlkem, see:
|
||||
* https://datatracker.ietf.org/doc/draft-ietf-tls-ecdhe-mlkem/
|
||||
*/
|
||||
WOLFSSL_SECP256R1MLKEM768 = 4587,
|
||||
WOLFSSL_X25519MLKEM768 = 4588,
|
||||
WOLFSSL_SECP384R1MLKEM1024 = 4589,
|
||||
|
||||
/* Taken from OQS's openssl provider, see:
|
||||
* https://github.com/open-quantum-safe/oqs-provider/blob/main/oqs-template/
|
||||
* oqs-kem-info.md
|
||||
*/
|
||||
WOLFSSL_P256_ML_KEM_512_OLD = 12103,
|
||||
WOLFSSL_P384_ML_KEM_768_OLD = 12104,
|
||||
WOLFSSL_P521_ML_KEM_1024_OLD = 12105,
|
||||
|
||||
WOLFSSL_SECP256R1MLKEM512 = 12107,
|
||||
WOLFSSL_SECP384R1MLKEM768 = 12108,
|
||||
WOLFSSL_SECP521R1MLKEM1024 = 12109,
|
||||
WOLFSSL_X25519MLKEM512 = 12214,
|
||||
WOLFSSL_X448MLKEM768 = 12215,
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static IntPtr unwrap_ctx(IntPtr ctx)
|
||||
{
|
||||
@@ -1288,6 +1376,36 @@ namespace wolfSSL.CSharp
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a key share entry for the specified group on the given SSL/TLS connection.
|
||||
/// </summary>
|
||||
/// <param name="ssl">Pointer to the SSL structure to use.</param>
|
||||
/// <param name="group">The key exchange group identifier to use for key share (e.g., TLS supported group ID).</param>
|
||||
/// <returns>1 on success</returns>
|
||||
public static int UseKeyShare(IntPtr ssl, NamedGroup group)
|
||||
{
|
||||
if (ssl == IntPtr.Zero)
|
||||
{
|
||||
return FAILURE;
|
||||
}
|
||||
try
|
||||
{
|
||||
IntPtr sslCtx = unwrap_ssl(ssl);
|
||||
if (sslCtx == IntPtr.Zero)
|
||||
{
|
||||
log(ERROR_LOG, "UseKeyShare ssl unwrap error");
|
||||
return FAILURE;
|
||||
}
|
||||
return wolfSSL_UseKeyShare(sslCtx, (ushort)group);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log(ERROR_LOG, "wolfSSL_UseKeyShare error " + e.ToString());
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Optional, can be used to set a custom receive function
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user