diff --git a/src/clu_main.c b/src/clu_main.c index fbff6b11..4aa64fb8 100644 --- a/src/clu_main.c +++ b/src/clu_main.c @@ -61,6 +61,7 @@ static const struct option mode_options[] = { {"ecc", no_argument, 0, WOLFCLU_ECC }, {"ed25519", no_argument, 0, WOLFCLU_ED25519 }, {"dilithium", no_argument, 0, WOLFCLU_DILITHIUM }, + {"ml-dsa", no_argument, 0, WOLFCLU_DILITHIUM }, {"xmss", no_argument, 0, WOLFCLU_XMSS }, {"xmssmt", no_argument, 0, WOLFCLU_XMSSMT }, {"dgst", no_argument, 0, WOLFCLU_DGST }, diff --git a/src/sign-verify/clu_sign_verify_setup.c b/src/sign-verify/clu_sign_verify_setup.c index 419e28bf..12c257fe 100644 --- a/src/sign-verify/clu_sign_verify_setup.c +++ b/src/sign-verify/clu_sign_verify_setup.c @@ -56,6 +56,9 @@ int wolfCLU_sign_verify_setup(int argc, char** argv) else if (wolfCLU_checkForArg("dilithium", 9, argc, argv) > 0) { algCheck = DILITHIUM_SIG_VER; } + else if (wolfCLU_checkForArg("ml-dsa", 6, argc, argv) > 0) { + algCheck = DILITHIUM_SIG_VER; + } else if (wolfCLU_checkForArg("xmss", 4, argc, argv) > 0) { algCheck = XMSS_SIG_VER; } @@ -227,7 +230,7 @@ int wolfCLU_sign_verify_setup(int argc, char** argv) } else if (algCheck == DILITHIUM_SIG_VER && verifyCheck == 0) { WOLFCLU_LOG(WOLFCLU_L0, "Please specify an output file when " - "signing with Dilithium."); + "signing with ML-DSA (Dilithium)."); wolfCLU_signHelp(algCheck); if (priv) XFREE(priv, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); diff --git a/src/tools/clu_funcs.c b/src/tools/clu_funcs.c index f5ce79f0..0245bbcb 100644 --- a/src/tools/clu_funcs.c +++ b/src/tools/clu_funcs.c @@ -83,6 +83,10 @@ static const struct option crypt_algo_options[] = { WOLFCLU_LOG(WOLFCLU_L0, "ecc Ecc signing and signature verification"); WOLFCLU_LOG(WOLFCLU_L0, "ecparam Generate an ECC key and parameters"); WOLFCLU_LOG(WOLFCLU_L0, "ed25519 Ed25519 signing and signature verification"); +#ifdef HAVE_DILITHIUM + WOLFCLU_LOG(WOLFCLU_L0, "ml-dsa ML-DSA signing and signature verification"); + WOLFCLU_LOG(WOLFCLU_L0, "dilithium Alias for ml-dsa"); +#endif WOLFCLU_LOG(WOLFCLU_L0, "enc / encrypt Encrypt a file or some user input"); WOLFCLU_LOG(WOLFCLU_L0, "hash Hash a file or input"); WOLFCLU_LOG(WOLFCLU_L0, "md5 Creates an MD5 hash"); @@ -138,6 +142,9 @@ static const struct option crypt_algo_options[] = { WOLFCLU_LOG(WOLFCLU_L0, "For ED25519 sign/ver: wolfssl -ed25519 -help"); WOLFCLU_LOG(WOLFCLU_L0, "For XMSS sign/ver: wolfssl -xmss -help"); WOLFCLU_LOG(WOLFCLU_L0, "For XMSS^MT sign/ver: wolfssl -xmssmt -help"); +#ifdef HAVE_DILITHIUM + WOLFCLU_LOG(WOLFCLU_L0, "For ML-DSA sign/ver: wolfssl -ml-dsa -help (or -dilithium -help)"); +#endif } /* @@ -473,8 +480,8 @@ void wolfCLU_genKeyHelp(void) ,"ecc" #endif #ifdef HAVE_DILITHIUM - ,"dilithium" ,"ml-dsa" + ,"dilithium" #endif #ifdef WOLFSSL_HAVE_XMSS ,"xmss" @@ -496,8 +503,12 @@ void wolfCLU_genKeyHelp(void) #ifdef HAVE_DILITHIUM WOLFCLU_LOG(WOLFCLU_L0, "wolfssl -genkey dilithium -level " "[2|3|5] -out mykey -outform der -output KEYPAIR"); + WOLFCLU_LOG(WOLFCLU_L0, "wolfssl -genkey dilithium -level " + "[2|3|5] -out mykey -outform pem -output KEYPAIR"); WOLFCLU_LOG(WOLFCLU_L0, "wolfssl -genkey ml-dsa -level " "[2|3|5] -out mykey -outform der -output KEYPAIR"); + WOLFCLU_LOG(WOLFCLU_L0, "wolfssl -genkey ml-dsa -level " + "[2|3|5] -out mykey -outform pem -output KEYPAIR"); #endif #ifdef WOLFSSL_HAVE_XMSS WOLFCLU_LOG(WOLFCLU_L0, "wolfssl -genkey xmss -height [10|16|20] -out mykey -outform raw" @@ -528,6 +539,10 @@ void wolfCLU_signHelp(int keyType) #ifdef HAVE_ECC ,"ecc" #endif + #ifdef HAVE_DILITHIUM + ,"ml-dsa" + ,"dilithium" + #endif #ifdef WOLFSSL_HAVE_XMSS ,"xmss" ,"xmssmt" @@ -548,6 +563,20 @@ void wolfCLU_signHelp(int keyType) WOLFCLU_LOG(WOLFCLU_L0, "***************************************************************"); break; #endif + #ifdef HAVE_DILITHIUM + case DILITHIUM_SIG_VER: + WOLFCLU_LOG(WOLFCLU_L0, "ML-DSA (Dilithium) Sign Usage:\n" + "wolfssl -ml-dsa -sign -inkey -inform \n" + " -in -out \n"); + WOLFCLU_LOG(WOLFCLU_L0, " -level [2|3|5] is set at key generation, not here.\n" + " PEM keys require '-inform pem' (default is der).\n" + " 'dilithium' is accepted as an alias for 'ml-dsa'.\n"); + WOLFCLU_LOG(WOLFCLU_L0, "EXAMPLE:\n" + "wolfssl -ml-dsa -sign -inkey ml-dsa-key-A.priv -inform pem\n" + " -in input.txt -out input.sign\n"); + WOLFCLU_LOG(WOLFCLU_L0, "***************************************************************"); + break; + #endif #ifdef HAVE_ED25519 case ED25519_SIG_VER: WOLFCLU_LOG(WOLFCLU_L0, "ED25519 Sign Usage: \nwolfssl -ed25519 -sign -inkey " @@ -591,6 +620,10 @@ void wolfCLU_verifyHelp(int keyType) { #ifdef HAVE_ECC ,"ecc" #endif + #ifdef HAVE_DILITHIUM + ,"ml-dsa" + ,"dilithium" + #endif #ifdef WOLFSSL_HAVE_XMSS ,"xmss" ,"xmssmt" @@ -616,6 +649,21 @@ void wolfCLU_verifyHelp(int keyType) { WOLFCLU_LOG(WOLFCLU_L0, "***************************************************************"); break; #endif + #ifdef HAVE_DILITHIUM + case DILITHIUM_SIG_VER: + WOLFCLU_LOG(WOLFCLU_L0, "ML-DSA (Dilithium) Verify Usage:\n" + "wolfssl -ml-dsa -verify -inkey -inform \n" + " -in -sigfile \n"); + WOLFCLU_LOG(WOLFCLU_L0, " Verifies with the public key (.pub).\n" + " PEM keys require '-inform pem' (default is der).\n" + " 'dilithium' is accepted as an alias for 'ml-dsa'.\n" + " (-pubin is not applicable; verification always uses the public key)\n"); + WOLFCLU_LOG(WOLFCLU_L0, "EXAMPLE:\n" + "wolfssl -ml-dsa -verify -inkey ml-dsa-key-A.pub -inform pem\n" + " -in input.txt -sigfile input.sign\n"); + WOLFCLU_LOG(WOLFCLU_L0, "***************************************************************"); + break; + #endif #ifdef HAVE_ED25519 case ED25519_SIG_VER: WOLFCLU_LOG(WOLFCLU_L0, "ED25519 Verifiy with Private Key" diff --git a/tests/genkey_sign_ver/genkey-sign-ver-test.py b/tests/genkey_sign_ver/genkey-sign-ver-test.py index de424482..db691d47 100644 --- a/tests/genkey_sign_ver/genkey-sign-ver-test.py +++ b/tests/genkey_sign_ver/genkey-sign-ver-test.py @@ -368,6 +368,38 @@ def test_sign_corrupted_key_fails(self): "output file must not be created when signing with " "corrupted key") + def test_ml_dsa_alias_der(self): + for level in [2, 3, 5]: + with self.subTest(level=level): + self._gen_sign_verify( + "ml-dsa", "mldsakey_alias", "mldsa-alias.sig", "der", + extra_genkey_args=["-level", str(level)], + skip_priv_verify=True, use_output_flag=True) + + def test_ml_dsa_alias_pem(self): + for level in [2, 3, 5]: + with self.subTest(level=level): + self._gen_sign_verify( + "ml-dsa", "mldsakey_alias", "mldsa-alias.sig", "pem", + extra_genkey_args=["-level", str(level)], + skip_priv_verify=True, use_output_flag=True) + + def test_ml_dsa_cross_alias(self): + """Keys generated with dilithium sign/verify with ml-dsa and vice-versa.""" + for level in [2, 3, 5]: + with self.subTest(level=level): + priv, pub = self._genkey("dilithium", "mldsakey_cross", + "der", ["-level", str(level)], + use_output_flag=True) + self._sign("ml-dsa", priv, "der", "mldsa-cross.sig") + self._verify_pub("ml-dsa", pub, "der", "mldsa-cross.sig") + + priv2, pub2 = self._genkey("ml-dsa", "mldsakey_cross2", + "der", ["-level", str(level)], + use_output_flag=True) + self._sign("dilithium", priv2, "der", "dil-cross.sig") + self._verify_pub("dilithium", pub2, "der", "dil-cross.sig") + @unittest.skipUnless(_has_algorithm("xmss"), "xmss not available") class XmssTest(_GenkeySignVerifyBase):