diff --git a/wrapper/CSharp/user_settings.h b/wrapper/CSharp/user_settings.h index 21a72a69b9..c38bc8da7f 100644 --- a/wrapper/CSharp/user_settings.h +++ b/wrapper/CSharp/user_settings.h @@ -85,6 +85,16 @@ #define ECC_TIMING_RESISTANT #define HAVE_COMP_KEY +/* Enable ML-KEM, ML-DSA */ +#define HAVE_MLKEM +#define WOLFSSL_WC_MLKEM +#define WOLFSSL_HAVE_MLKEM +#define WOLFSSL_DTLS_CH_FRAG +#define HAVE_DILITHIUM +#define WOLFSSL_WC_DILITHIUM +#define WOLFSSL_SHAKE128 +#define WOLFSSL_SHAKE256 + /* Disable features */ #define NO_PSK diff --git a/wrapper/CSharp/wolfCrypt-Test/wolfCrypt-Test.cs b/wrapper/CSharp/wolfCrypt-Test/wolfCrypt-Test.cs index b9a1be3519..253de8199b 100644 --- a/wrapper/CSharp/wolfCrypt-Test/wolfCrypt-Test.cs +++ b/wrapper/CSharp/wolfCrypt-Test/wolfCrypt-Test.cs @@ -648,6 +648,289 @@ private static void curve25519_test() 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, privA, cipherText, sharedSecretA, sharedSecretB; + + 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("Generate Key Pair B..."); + keyB = wolfcrypt.MlKemMakeKey(type, heap, devId); + if (keyB == IntPtr.Zero) + { + ret = -1; + Console.Error.WriteLine("Failed to generate key pair B."); + } + } + 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."); + } + } + if (ret == 0) + { + ret = wolfcrypt.MlKemEncodePrivateKey(keyA, out privA); + if (ret != 0) + { + Console.Error.WriteLine("Failed to encode private key of A."); + } + } + 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."); + } + } + 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."); + } + } + if (ret == 0) + { + ret = wolfcrypt.MlKemDecodePublicKey(keyB, pubA); + if (ret != 0) + { + Console.Error.WriteLine("Failed to decode public key of B."); + } + } + 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."); + } + } + if (ret == 0) + { + Console.WriteLine("ML-KEM Decapsulation test passed."); + } + + /* DEBUG CODE FOR REVIEW */ + ret = BAD_FUNC_ARG; + + /* Check */ + if (ret == 0) + { + Console.WriteLine("Comparing Shared Secrets..."); + if (!wolfcrypt.ByteArrayVerify(sharedSecretA, sharedSecretB)) + { + Console.Error.WriteLine("Shared secrets do not match."); + } + else + { + Console.WriteLine("ML-KEM shared secret match."); + } + } + } + catch + { + // todo: + // ........ https://github.com/wolfSSL/wolfssl/pull/9040#discussion_r2386193630 + // ........ https://github.com/wolfSSL/wolfssl/pull/9040#discussion_r2386194020 + } + finally + { + /* Cleanup */ + if (keyA != IntPtr.Zero) + { + ret = wolfcrypt.MlKemFreeKey(keyA); + if (ret != 0) + { + Console.Error.WriteLine("Failed to free MlKem key A. Error code: " + ret); + } + } + if (keyB != IntPtr.Zero) + { + ret = wolfcrypt.MlKemFreeKey(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.MlDsaTypes type) + { + int ret = 0; + IntPtr key = IntPtr.Zero; + IntPtr heap = IntPtr.Zero; + int devId = wolfcrypt.INVALID_DEVID; + byte[] privateKey; + byte[] publicKey; + byte[] message = Encoding.UTF8.GetBytes("This is some data to sign with ML-DSA"); + byte[] signature; + + try + { + Console.WriteLine("\nStarting " + type + " shared secret test ..."); + + /* Generate Key */ + Console.WriteLine("Testing ML-DSA Key Generation..."); + key = wolfcrypt.DilithiumMakeKey(heap, devId, type); + if (key == IntPtr.Zero) + { + ret = -1; + Console.Error.WriteLine("DilithiumMakeKey failed"); + } + if (ret == 0) + { + Console.WriteLine("ML-DSA Key Generation test passed."); + } + + /* Export */ + if (ret == 0) + { + Console.WriteLine("Testing ML-DSA Key Export..."); + ret = DilithiumExportPrivateKey(key, out privateKey); + if (ret != 0) + { + Console.Error.WriteLine("DilithiumExportPrivateKey failed"); + } + } + if (ret == 0) + { + ret = DilithiumExportPublicKey(key, out publicKey); + if (ret != 0) + { + Console.Error.WriteLine("DilithiumExportPublicKey failed"); + } + } + if (ret == 0) + { + Console.WriteLine("ML-DSA Key Export test passed."); + } + + /* Import */ + if (ret == 0) + { + Console.WriteLine("Testing ML-DSA Key Import..."); + ret = DilithiumImportPrivateKey(privateKey, key); + if (ret != 0) + { + Console.Error.WriteLine("DilithiumImportPrivateKey failed"); + } + } + if (ret == 0) + { + ret = DilithiumImportPublicKey(publicKey, key); + if (ret != 0) + { + Console.Error.WriteLine("DilithiumImportPrivateKey failed"); + } + } + if (ret == 0) + { + Console.WriteLine("ML-DSA Key Import test passed."); + } + + /* Sign */ + if (ret == 0) + { + Console.WriteLine("Testing ML-DSA Signature Creation..."); + ret = wolfcrypt.DilithiumSignMsg(key, message, out signature); + if (ret != 0) + { + Console.Error.WriteLine("DilithiumSign failed"); + } + } + if (ret == 0) + { + Console.WriteLine($"ML-DSA Signature Creation test passed. Signature Length: {signature.Length}"); + } + + /* Verify */ + if (ret == 0) + { + Console.WriteLine("Testing ML-DSA Signature Verification..."); + ret = wolfcrypt.DilithiumVerifyMsg(key, message, signature); + if (ret != 0) + { + Console.Error.WriteLine("DilithiumVerify failed"); + } + } + if (ret == 0) + { + Console.WriteLine("ML-DSA Signature Verification test passed."); + } + } + catch + { + // todo: + // ........ https://github.com/wolfSSL/wolfssl/pull/9040#discussion_r2386193630 + // ........ https://github.com/wolfSSL/wolfssl/pull/9040#discussion_r2386194020 + } + finally + { + if (key != IntPtr.Zero) + { + ret = wolfcrypt.DilithiumFreeKey(key); + if (ret != 0) + { + Console.Error.WriteLine("Failed to free Dilithium key. Error code: " + ret); + } + } + } + + } /* END mldsa_test */ + private static void aes_gcm_test() { IntPtr aes = IntPtr.Zero; @@ -904,6 +1187,18 @@ public static void Main(string[] args) 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.MlDsaTypes.ML_DSA_44); /* ML-DSA test */ + mldsa_test(wolfcrypt.MlDsaTypes.ML_DSA_65); /* ML-DSA test */ + mldsa_test(wolfcrypt.MlDsaTypes.ML_DSA_87); /* ML-DSA test */ + Console.WriteLine("\nStarting AES-GCM test"); aes_gcm_test(); /* AES_GCM test */ diff --git a/wrapper/CSharp/wolfSSL_CSharp/wolfCrypt.cs b/wrapper/CSharp/wolfSSL_CSharp/wolfCrypt.cs index 4f58139c25..4f0913f8ec 100644 --- a/wrapper/CSharp/wolfSSL_CSharp/wolfCrypt.cs +++ b/wrapper/CSharp/wolfSSL_CSharp/wolfCrypt.cs @@ -428,6 +428,147 @@ public class wolfcrypt private extern static int wc_curve25519_export_private_raw(IntPtr key, IntPtr outPrivKey, IntPtr outPubKey); #endif + /******************************** + * ML-KEM + */ +#if WindowsCE + [DllImport(wolfssl_dll)] + private static extern int wc_MlKemKey_CipherTextSize(IntPtr key, ref uint len); + [DllImport(wolfssl_dll)] + private static extern int wc_MlKemKey_SharedSecretSize(IntPtr key, ref uint len); + [DllImport(wolfssl_dll)] + private static extern int wc_MlKemKey_PrivateKeySize(IntPtr key, ref uint len); + [DllImport(wolfssl_dll)] + private static extern int wc_MlKemKey_PublicKeySize(IntPtr key, ref uint len); + [DllImport(wolfssl_dll)] + private static extern IntPtr wc_MlKemKey_New(int type, IntPtr heap, int devId); + [DllImport(wolfssl_dll)] + private static extern int wc_MlKemKey_Delete(IntPtr key, ref IntPtr key_p); + [DllImport(wolfssl_dll)] + private static extern int wc_MlKemKey_Init(IntPtr key, int type, IntPtr heap, int devId); + [DllImport(wolfssl_dll)] + private static extern void wc_MlKemKey_Free(IntPtr key); + [DllImport(wolfssl_dll)] + private static extern int wc_MlKemKey_MakeKey(IntPtr key, IntPtr rng); + [DllImport(wolfssl_dll)] + private static extern int wc_MlKemKey_MakeKeyWithRandom(IntPtr key, byte[] rand, int len); + [DllImport(wolfssl_dll)] + private static extern int wc_MlKemKey_EncodePublicKey(IntPtr key, byte[] output, uint len); + [DllImport(wolfssl_dll)] + private static extern int wc_MlKemKey_DecodePublicKey(IntPtr key, byte[] input, uint len); + [DllImport(wolfssl_dll)] + private static extern int wc_MlKemKey_Encapsulate(IntPtr key, byte[] ct, byte[] ss, IntPtr rng); + [DllImport(wolfssl_dll)] + private static extern int wc_MlKemKey_EncapsulateWithRandom(IntPtr key, byte[] ct, byte[] ss, byte[] rand, int len); + [DllImport(wolfssl_dll)] + private static extern int wc_MlKemKey_Decapsulate(IntPtr key, byte[] ss, byte[] ct, uint len); + [DllImport(wolfssl_dll)] + private static extern int wc_MlKemKey_EncodePrivateKey(IntPtr key, byte[] output, uint len); + [DllImport(wolfssl_dll)] + private static extern int wc_MlKemKey_DecodePrivateKey(IntPtr key, byte[] input, uint len); +#else + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlKemKey_CipherTextSize(IntPtr key, ref uint len); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlKemKey_SharedSecretSize(IntPtr key, ref uint len); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlKemKey_PrivateKeySize(IntPtr key, ref uint len); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlKemKey_PublicKeySize(IntPtr key, ref uint len); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr wc_MlKemKey_New(int type, IntPtr heap, int devId); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlKemKey_Delete(IntPtr key, ref IntPtr key_p); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlKemKey_Init(IntPtr key, int type, IntPtr heap, int devId); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern void wc_MlKemKey_Free(IntPtr key); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlKemKey_MakeKey(IntPtr key, IntPtr rng); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlKemKey_MakeKeyWithRandom(IntPtr key, byte[] rand, int len); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlKemKey_EncodePublicKey(IntPtr key, byte[] output, uint len); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlKemKey_DecodePublicKey(IntPtr key, byte[] input, uint len); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlKemKey_Encapsulate(IntPtr key, byte[] ct, byte[] ss, IntPtr rng); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlKemKey_EncapsulateWithRandom(IntPtr key, byte[] ct, byte[] ss, byte[] rand, int len); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlKemKey_Decapsulate(IntPtr key, byte[] ss, byte[] ct, uint len); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlKemKey_EncodePrivateKey(IntPtr key, byte[] output, uint len); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlKemKey_DecodePrivateKey(IntPtr key, byte[] input, uint len); +#endif + + /******************************** + * ML-DSA + */ +#if WindowsCE + [DllImport(wolfssl_dll)] + private static extern IntPtr wc_dilithium_new(IntPtr heap, int devId); + [DllImport(wolfssl_dll)] + private static extern int wc_dilithium_delete(IntPtr key, ref IntPtr key_p); + [DllImport(wolfssl_dll)] + private static extern int wc_dilithium_init_ex(IntPtr key, IntPtr heap, int devId); + [DllImport(wolfssl_dll)] + private static extern int wc_dilithium_set_level(IntPtr key, byte level); + [DllImport(wolfssl_dll)] + private static extern void wc_dilithium_free(IntPtr key); + [DllImport(wolfssl_dll)] + private static extern int wc_dilithium_make_key(IntPtr key, IntPtr rng); + [DllImport(wolfssl_dll)] + private static extern int wc_dilithium_export_private(IntPtr key, byte[] output, ref uint outLen); + [DllImport(wolfssl_dll)] + private static extern int wc_dilithium_import_private(byte[] priv, uint privSz, IntPtr key); + [DllImport(wolfssl_dll)] + private static extern int wc_dilithium_export_public(IntPtr key, byte[] output, ref uint outLen); + [DllImport(wolfssl_dll)] + private static extern int wc_dilithium_import_public(byte[] input, uint inputLen, IntPtr key); + [DllImport(wolfssl_dll)] + private static extern int wc_dilithium_sign_msg(byte[] msg, uint msgLen, byte[] sig, ref uint sigLen, IntPtr key, IntPtr rng); + [DllImport(wolfssl_dll)] + private static extern int wc_dilithium_verify_msg(byte[] sig, uint sigLen, byte[] msg, uint msgLen, ref int res, IntPtr key); + [DllImport(wolfssl_dll)] + private static extern int wc_MlDsaKey_GetPrivLen(IntPtr key, ref int len); + [DllImport(wolfssl_dll)] + private static extern int wc_MlDsaKey_GetPubLen(IntPtr key, ref int len); + [DllImport(wolfssl_dll)] + private static extern int wc_MlDsaKey_GetSigLen(IntPtr key, ref int len); +#else + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr wc_dilithium_new(IntPtr heap, int devId); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_dilithium_delete(IntPtr key, ref IntPtr key_p); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_dilithium_init_ex(IntPtr key, IntPtr heap, int devId); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_dilithium_set_level(IntPtr key, byte level); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern void wc_dilithium_free(IntPtr key); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_dilithium_make_key(IntPtr key, IntPtr rng); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_dilithium_export_private(IntPtr key, byte[] output, ref uint outLen); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_dilithium_import_private(byte[] priv, uint privSz, IntPtr key); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_dilithium_export_public(IntPtr key, byte[] output, ref uint outLen); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_dilithium_import_public(byte[] input, uint inputLen, IntPtr key); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_dilithium_sign_msg(byte[] msg, uint msgLen, byte[] sig, ref uint sigLen, IntPtr key, IntPtr rng); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_dilithium_verify_msg(byte[] sig, uint sigLen, byte[] msg, uint msgLen, ref int res, IntPtr key); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlDsaKey_GetPrivLen(IntPtr key, ref int len); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlDsaKey_GetPubLen(IntPtr key, ref int len); + [DllImport(wolfssl_dll, CallingConvention = CallingConvention.Cdecl)] + private static extern int wc_MlDsaKey_GetSigLen(IntPtr key, ref int len); +#endif /******************************** * AES-GCM @@ -2709,6 +2850,710 @@ public static void Curve25519ExportKeyRaw(IntPtr key, out byte[] privateKey, out /* END RAW Curve25519 */ + /*********************************************************************** + * ML-KEM + **********************************************************************/ + + // These APIs work by adding several options to wolfCrypt. + // Please refer to `../user_settings.h`. + + /// + /// Create a new ML-KEM key pair and initialize it with random values + /// + /// ML-KEM parameter set type + /// Heap pointer for memory allocation + /// Device ID (if applicable) + /// Pointer to the MlKem key structure, or IntPtr.Zero on failure + public static IntPtr MlKemMakeKey(MlKemTypes type, IntPtr heap, int devId) + { + int ret = 0; + IntPtr key = IntPtr.Zero; + IntPtr rng = IntPtr.Zero; + bool success = false; + + try + { + key = wc_MlKemKey_New((int)type, heap, devId); + if (key == IntPtr.Zero) + { + log(ERROR_LOG, "Failed to allocate or initialize MlKem key."); + return IntPtr.Zero; + } + + rng = RandomNew(); + if (rng == IntPtr.Zero) + { + log(ERROR_LOG, "Failed to create RNG for MlKem key."); + return IntPtr.Zero; + } + + ret = wc_MlKemKey_MakeKey(key, rng); + if (ret != 0) + { + log(ERROR_LOG, "Failed to make MlKem key. Error code: " + ret); + return IntPtr.Zero; + } + + success = true; + return key; + } + catch (Exception ex) + { + log(ERROR_LOG, "MlKem key creation exception: " + ex.ToString()); + return IntPtr.Zero; + } + finally + { + if (rng != IntPtr.Zero) + { + RandomFree(rng); + } + if (!success && key != IntPtr.Zero) + { + ret = MlKemFreeKey(key); + if (ret != 0) + { + log(ERROR_LOG, "Failed to free MlKem key. Error code: " + ret); + } + } + } + } + + /// + /// Free a MlKem key structure and release its memory + /// + /// Pointer to the MlKem key structure + /// 0 on success, negative value on error. + public static int MlKemFreeKey(IntPtr key) + { + int ret = 0; + IntPtr heap = IntPtr.Zero; + + if (key != IntPtr.Zero) + { + ret = wc_MlKemKey_Delete(key, ref heap); + key = IntPtr.Zero; + } + return ret; + } + + /// + /// Encode the ML-KEM public key to a byte array. + /// + /// Pointer to the MlKem key structure. + /// Output byte array containing the encoded public key. + /// 0 on success, negative value on error. + public static int MlKemEncodePublicKey(IntPtr key, out byte[] publicKey) + { + publicKey = null; + int ret = 0; + uint pubLen = 0; + + if (key == IntPtr.Zero) + { + return BAD_FUNC_ARG; + } + + try + { + ret = wc_MlKemKey_PublicKeySize(key, ref pubLen); + if (pubLen == 0) + { + log(ERROR_LOG, "Failed to get MlKem public key length. Error code: " + ret); + return ret; + } + + publicKey = new byte[pubLen]; + ret = wc_MlKemKey_EncodePublicKey(key, publicKey, pubLen); + if (ret != 0) + { + log(ERROR_LOG, "Failed to encode MlKem public key. Error code: " + ret); + publicKey = null; + return ret; + } + } + catch (Exception e) + { + log(ERROR_LOG, "MlKem encode public key exception: " + e.ToString()); + publicKey = null; + return EXCEPTION_E; + } + return SUCCESS; + } + + /// + /// Encode the ML-KEM private key to a byte array. + /// + /// Pointer to the MlKem key structure. + /// Output byte array containing the encoded private key. + /// 0 on success, negative value on error. + public static int MlKemEncodePrivateKey(IntPtr key, out byte[] privateKey) + { + privateKey = null; + int ret = 0; + uint privLen = 0; + + if (key == IntPtr.Zero) + { + return BAD_FUNC_ARG; + } + + try + { + ret = wc_MlKemKey_PrivateKeySize(key, ref privLen); + if (ret !=0 || privLen == 0) + { + log(ERROR_LOG, "Failed to get MlKem private key length. Error code: " + ret); + return ret; + } + + privateKey = new byte[privLen]; + ret = wc_MlKemKey_EncodePrivateKey(key, privateKey, privLen); + if (ret != 0) + { + log(ERROR_LOG, "Failed to encode MlKem private key. Error code: " + ret); + privateKey = null; + return ret; + } + } + catch (Exception e) + { + log(ERROR_LOG, "MlKem encode private key exception: " + e.ToString()); + privateKey = null; + return EXCEPTION_E; + } + return SUCCESS; + } + + /// + /// Decode a ML-KEM public key from a byte array. + /// + /// Pointer to the MlKem key structure. + /// Encoded public key as byte array. + /// 0 on success, negative value on error. + public static int MlKemDecodePublicKey(IntPtr key, byte[] publicKey) + { + int ret = 0; + uint pubLen = 0; + + if (key == IntPtr.Zero) + { + return BAD_FUNC_ARG; + } + + try + { + ret = wc_MlKemKey_PublicKeySize(key, ref pubLen); + if (ret != 0 || pubLen == 0) + { + log(ERROR_LOG, "Failed to get MlKem public key length. Error code: " + ret); + return ret; + } + + ret = wc_MlKemKey_DecodePublicKey(key, publicKey, pubLen); + if (ret != 0) + { + log(ERROR_LOG, "Failed to decode MlKem public key. Error code: " + ret); + return ret; + } + } + catch (Exception ex) + { + log(ERROR_LOG, "MlKem decode public key exception: " + ex.ToString()); + return EXCEPTION_E; + } + return SUCCESS; + } + + /// + /// Decode a ML-KEM private key from a byte array. + /// + /// Pointer to the MlKem key structure. + /// Encoded private key as byte array. + /// 0 on success, negative value on error. + public static int MlKemDecodePrivateKey(IntPtr key, byte[] privateKey) + { + int ret = 0; + uint privLen = 0; + + if (key == IntPtr.Zero) + { + return BAD_FUNC_ARG; + } + + try + { + ret = wc_MlKemKey_PrivateKeySize(key, ref privLen); + if (privLen == 0) + { + log(ERROR_LOG, "Failed to get MlKem private key length. Error code: " + ret); + return ret; + } + + ret = wc_MlKemKey_DecodePrivateKey(key, privateKey, privLen); + if (ret != 0) + { + log(ERROR_LOG, "Failed to decode MlKem private key. Error code: " + ret); + return ret; + } + } + catch (Exception ex) + { + log(ERROR_LOG, "MlKem decode private key exception: " + ex.ToString()); + return EXCEPTION_E; + } + return SUCCESS; + } + + /// + /// Perform ML-KEM encapsulation to generate a ciphertext and shared secret + /// + /// Pointer to the MlKem key structure + /// Output buffer for the ciphertext + /// Output buffer for the shared secret + /// 0 on success, otherwise an error code + public static int MlKemEncapsulate(IntPtr key, out byte[] ct, out byte[] ss) + { + int ret; + ct = null; + ss = null; + uint ctLen = 0; + uint ssLen = 0; + IntPtr rng; + + if (key == IntPtr.Zero) + { + return BAD_FUNC_ARG; + } + + try + { + ret = wc_MlKemKey_CipherTextSize(key, ref ctLen); + if (ret != 0) + { + log(ERROR_LOG, "Failed to determine ciphertext length. Error code: " + ret); + return ret; + } + ret = wc_MlKemKey_SharedSecretSize(key, ref ssLen); + if (ret != 0) + { + log(ERROR_LOG, "Failed to determine shared secret length. Error code: " + ret); + return ret; + } + + ct = new byte[ctLen]; + ss = new byte[ssLen]; + rng = RandomNew(); + ret = wc_MlKemKey_Encapsulate(key, ct, ss, rng); + RandomFree(rng); + if (ret != 0) + { + log(ERROR_LOG, "Failed to encapsulate MlKem key. Error code: " + ret); + return ret; + } + } + catch (Exception e) + { + log(ERROR_LOG, "MlKem encapsulate exception: " + e.ToString()); + return EXCEPTION_E; + } + return SUCCESS; + } + + /// + /// Perform ML-KEM decapsulation to recover the shared secret from ciphertext + /// + /// Pointer to the MlKem key structure + /// Ciphertext buffer + /// Output buffer for the shared secret + /// 0 on success, otherwise an error code + public static int MlKemDecapsulate(IntPtr key, byte[] ct, out byte[] ss) + { + int ret; + ss = null; + uint ssLen = 0; + + if (key == IntPtr.Zero || ct == null) + { + return BAD_FUNC_ARG; + } + + try + { + ret = wc_MlKemKey_SharedSecretSize(key, ref ssLen); + if (ret != 0) + { + log(ERROR_LOG, "Failed to determine shared secret length. Error code: " + ret); + return ret; + } + ss = new byte[ssLen]; + ret = wc_MlKemKey_Decapsulate(key, ss, ct, (uint)ct.Length); + if (ret != 0) + { + log(ERROR_LOG, "Failed to decapsulate MlKem key. Error code: " + ret); + return ret; + } + } + catch (Exception e) + { + log(ERROR_LOG, "MlKem decapsulate exception: " + e.ToString()); + return EXCEPTION_E; + } + return SUCCESS; + } + + /******************************** + * ENUMS + */ + public enum MlKemTypes + { + ML_KEM_512 = 0, + ML_KEM_768 = 1, + ML_KEM_1024 = 2 + } + /* END ML-KEM */ + + + /*********************************************************************** + * ML-DSA + **********************************************************************/ + + // These APIs work by adding several options to wolfCrypt. + // Please refer to `../user_settings.h`. + + /// + /// Create a new Dilithium key pair and initialize it with random values + /// + /// Heap pointer for memory allocation + /// Device ID (if applicable) + /// Dilithium security level + /// Pointer to the Dilithium key structure, or IntPtr.Zero on failure + public static IntPtr DilithiumMakeKey(IntPtr heap, int devId, MlDsaTypes type) + { + IntPtr key = IntPtr.Zero; + IntPtr rng = IntPtr.Zero; + int ret; + bool success = false; + + try + { + key = wc_dilithium_new(heap, devId); + if (key == IntPtr.Zero) + { + log(ERROR_LOG, "Failed to allocate and initialize Dilithium key."); + return IntPtr.Zero; + } + + ret = wc_dilithium_set_level(key, (byte)type); + if (ret != 0) + { + log(ERROR_LOG, "Failed to set Dilithium level. Error code: " + ret); + return IntPtr.Zero; + } + + rng = RandomNew(); + if (rng == IntPtr.Zero) + { + log(ERROR_LOG, "Failed to create RNG for Dilithium key."); + return IntPtr.Zero; + } + + ret = wc_dilithium_make_key(key, rng); + if (ret != 0) + { + log(ERROR_LOG, "Failed to make Dilithium key. Error code: " + ret); + return IntPtr.Zero; + } + + success = true; + return key; + } + catch (Exception ex) + { + log(ERROR_LOG, "Dilithium key creation exception: " + ex.ToString()); + return IntPtr.Zero; + } + finally + { + if (rng != IntPtr.Zero) + { + RandomFree(rng); + } + if (!success && key != IntPtr.Zero) + { + ret = DilithiumFreeKey(key); + if (ret != 0) + { + log(ERROR_LOG, "Failed to free Dilithium key. Error code: " + ret); + } + } + } + } + + /// + /// Free a Dilithium key structure and release its memory + /// + /// Pointer to the Dilithium key structure + /// 0 on success, negative value on error. + public static int DilithiumFreeKey(IntPtr key) + { + int ret = 0; + IntPtr heap = IntPtr.Zero; + + if (key != IntPtr.Zero) + { + ret = wc_dilithium_delete(key, ref heap); + key = IntPtr.Zero; + } + return ret; + } + + /// + /// Import a Dilithium public key from a byte array. + /// + /// Byte array containing the public key (big-endian). + /// Pointer to the Dilithium key structure (must be initialized). + /// 0 on success, negative value on error. + public static int DilithiumImportPublicKey(byte[] publicKey, IntPtr key) + { + if (publicKey == null || key == IntPtr.Zero) + { + return BAD_FUNC_ARG; + } + + try + { + return wc_dilithium_import_public(publicKey, (uint)publicKey.Length, key); + } + catch (Exception e) + { + log(ERROR_LOG, "Dilithium import public key exception: " + e.ToString()); + return EXCEPTION_E; + } + } + + /// + /// Import a Dilithium private key from a byte array. + /// + /// Byte array containing the private key. + /// Pointer to the Dilithium key structure (must be initialized and have level set). + /// 0 on success, negative value on error. + public static int DilithiumImportPrivateKey(byte[] privateKey, IntPtr key) + { + if (privateKey == null || key == IntPtr.Zero) + { + return BAD_FUNC_ARG; + } + + try + { + return wc_dilithium_import_private(privateKey, (uint)privateKey.Length, key); + } + catch (Exception e) + { + log(ERROR_LOG, "Dilithium import private key exception: " + e.ToString()); + return EXCEPTION_E; + } + } + + /// + /// Export a Dilithium private key to a byte array. + /// + /// Pointer to the Dilithium key structure. + /// Output byte array containing the private key. + /// 0 on success, negative value on error. + public static int DilithiumExportPrivateKey(IntPtr key, out byte[] privateKey) + { + privateKey = null; + int ret = 0; + int privLen = 0; + uint outLen; + + if (key == IntPtr.Zero) + { + return BAD_FUNC_ARG; + } + + try + { + ret = wc_MlDsaKey_GetPrivLen(key, ref privLen); + if (privLen == 0) + { + log(ERROR_LOG, "Failed to get Dilithium private key length. Error code: " + ret); + return ret; + } + + privateKey = new byte[privLen]; + outLen = (uint)privLen; + ret = wc_dilithium_export_private(key, privateKey, ref outLen); + if (ret != 0) + { + log(ERROR_LOG, "Failed to export Dilithium private key. Error code: " + ret); + privateKey = null; + return ret; + } + if (outLen != privLen) + { + Array.Resize(ref privateKey, (int)outLen); + } + } + catch (Exception e) + { + log(ERROR_LOG, "Dilithium export private key exception: " + e.ToString()); + privateKey = null; + return EXCEPTION_E; + } + return SUCCESS; + } + + /// + /// Export a Dilithium public key to a byte array. + /// + /// Pointer to the Dilithium key structure. + /// Output byte array containing the public key. + /// 0 on success, negative value on error. + public static int DilithiumExportPublicKey(IntPtr key, out byte[] publicKey) + { + publicKey = null; + int ret = 0; + int pubLen = 0; + uint outLen; + + if (key == IntPtr.Zero) + { + return BAD_FUNC_ARG; + } + + try + { + ret = wc_MlDsaKey_GetPubLen(key, ref pubLen); + if (pubLen <= 0) + { + log(ERROR_LOG, "Failed to get Dilithium public key length. Error code: " + ret); + return ret; + } + + publicKey = new byte[pubLen]; + outLen = (uint)pubLen; + ret = wc_dilithium_export_public(key, publicKey, ref outLen); + if (ret != 0) + { + log(ERROR_LOG, "Failed to export Dilithium public key. Error code: " + ret); + publicKey = null; + return ret; + } + if (outLen != (uint)pubLen) + { + Array.Resize(ref publicKey, (int)outLen); + } + } + catch (Exception e) + { + log(ERROR_LOG, "Dilithium export public key exception: " + e.ToString()); + publicKey = null; + return EXCEPTION_E; + } + return SUCCESS; + } + + /// + /// Sign a message using a Dilithium private key + /// + /// Pointer to the Dilithium key structure + /// Message to sign + /// Output byte array for the signature + /// 0 on success, otherwise an error code + public static int DilithiumSignMsg(IntPtr key, byte[] msg, out byte[] sig) + { + int ret; + int sigLen = 0; + uint outLen; + sig = null; + IntPtr rng; + + if (key == IntPtr.Zero || msg == null) + { + return BAD_FUNC_ARG; + } + + try + { + ret = wc_MlDsaKey_GetSigLen(key, ref sigLen); + if (sigLen <= 0) + { + log(ERROR_LOG, "Failed to get Dilithium signature length. Error code: " + ret); + return ret; + } + + sig = new byte[sigLen]; + outLen = (uint)sigLen; + rng = RandomNew(); + ret = wc_dilithium_sign_msg(msg, (uint)msg.Length, sig, ref outLen, key, rng); + RandomFree(rng); + if (ret != 0) + { + log(ERROR_LOG, "Failed to sign message with Dilithium key. Error code: " + ret); + return ret; + } + if (outLen != (uint)sigLen) + { + Array.Resize(ref sig, (int)outLen); + } + } + catch (Exception e) + { + log(ERROR_LOG, "Dilithium sign message exception: " + e.ToString()); + return EXCEPTION_E; + } + return SUCCESS; + } + + /// + /// Verify a Dilithium signature + /// + /// Pointer to the Dilithium key structure + /// Message that was signed + /// Signature to verify + /// 0 if the signature is valid, otherwise an error code + public static int DilithiumVerifyMsg(IntPtr key, byte[] msg, byte[] sig) + { + int ret; + int res = 0; + + if (key == IntPtr.Zero || msg == null || sig == null) + { + return BAD_FUNC_ARG; + } + + try + { + ret = wc_dilithium_verify_msg(sig, (uint)sig.Length, msg, (uint)msg.Length, ref res, key); + if (res != 1) + { + log(ERROR_LOG, "Failed to verify message with Dilithium key. Error code: " + ret); + return ret; + } + } + catch (Exception e) + { + log(ERROR_LOG, "Dilithium verify message exception: " + e.ToString()); + return EXCEPTION_E; + } + return SUCCESS; + } + + /******************************** + * ENUMS + */ + public enum MlDsaTypes + { + ML_DSA_44 = 2, + ML_DSA_65 = 3, + ML_DSA_87 = 5 + } + /* END ML-DSA */ + + /*********************************************************************** * AES-GCM **********************************************************************/ diff --git a/wrapper/CSharp/wolfSSL_CSharp/wolfSSL.cs b/wrapper/CSharp/wolfSSL_CSharp/wolfSSL.cs index 0ee293f2f5..56c9502468 100644 --- a/wrapper/CSharp/wolfSSL_CSharp/wolfSSL.cs +++ b/wrapper/CSharp/wolfSSL_CSharp/wolfSSL.cs @@ -509,6 +509,8 @@ public void free() 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 @@ public void free() 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 @@ public void free() 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_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_MlKem_LEVEL1 = 570, /* MlKem_512 */ + WOLFSSL_MlKem_LEVEL3 = 572, /* MlKem_572 */ + WOLFSSL_MlKem_LEVEL5 = 573, /* MlKem_573 */ + + WOLFSSL_P256_MlKem_LEVEL1 = 12090, + WOLFSSL_P384_MlKem_LEVEL3 = 12092, + WOLFSSL_P521_MlKem_LEVEL5 = 12093, + WOLFSSL_X25519_MlKem_LEVEL1 = 12089, + WOLFSSL_X448_MlKem_LEVEL3 = 12176, + WOLFSSL_X25519_MlKem_LEVEL3 = 25497, + WOLFSSL_P256_MlKem_LEVEL3 = 25498, + + /* Taken from draft-connolly-tls-mlkem-key-agreement, see: + * https://github.com/dconnolly/draft-connolly-tls-mlkem-key-agreement/ + */ + WOLFSSL_ML_KEM_512 = 512, + WOLFSSL_ML_KEM_768 = 513, + WOLFSSL_ML_KEM_1024 = 514, + + /* Taken from draft-kwiatkowski-tls-ecdhe-mlkem. see: + * https://github.com/post-quantum-cryptography/ + * draft-kwiatkowski-tls-ecdhe-mlkem/ + */ + WOLFSSL_P256_ML_KEM_768 = 4587, + WOLFSSL_X25519_ML_KEM_768 = 4588, + WOLFSSL_P384_ML_KEM_1024 = 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_P256_ML_KEM_512 = 12107, + WOLFSSL_P384_ML_KEM_768 = 12108, + WOLFSSL_P521_ML_KEM_1024 = 12109, + WOLFSSL_X25519_ML_KEM_512 = 12214, + WOLFSSL_X448_ML_KEM_768 = 12215, + + WOLF_ENUM_DUMMY_LAST_ELEMENT = 0 + } + + private static IntPtr unwrap_ctx(IntPtr ctx) { @@ -1288,6 +1376,36 @@ public static int shutdown(IntPtr ssl) } + /// + /// Creates a key share entry for the specified group on the given SSL/TLS connection. + /// + /// Pointer to the SSL structure to use. + /// The key exchange group identifier to use for key share (e.g., TLS supported group ID). + /// 1 on success + 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; + } + } + + /// /// Optional, can be used to set a custom receive function ///