diff --git a/src/x509/clu_request_setup.c b/src/x509/clu_request_setup.c index 80494618..aa2d5506 100644 --- a/src/x509/clu_request_setup.c +++ b/src/x509/clu_request_setup.c @@ -981,19 +981,18 @@ int wolfCLU_requestSetup(int argc, char** argv) } if (ret == WOLFCLU_SUCCESS && doVerify) { - WOLFSSL_EVP_PKEY* pubKey = pkey; /* get public key from req if not passed in */ - if (pubKey == NULL) { - pubKey = wolfSSL_X509_get_pubkey(x509); + if (pkey == NULL) { + pkey = wolfSSL_X509_get_pubkey(x509); } - if (pubKey == NULL) { + if (pkey == NULL) { wolfCLU_LogError("Error getting the public key to verify"); ret = WOLFCLU_FATAL_ERROR; } else { - if (wolfSSL_X509_REQ_verify(x509, pubKey) == 1) { + if (wolfSSL_X509_REQ_verify(x509, pkey) == 1) { WOLFCLU_LOG(WOLFCLU_L0, "verify OK"); } else { diff --git a/tests/base64/base64-test.py b/tests/base64/base64-test.py index 671ca8a3..3934e573 100644 --- a/tests/base64/base64-test.py +++ b/tests/base64/base64-test.py @@ -103,6 +103,17 @@ def test_stdin_input(self): self.assertEqual(result.returncode, 0, "Couldn't parse input from stdin") + def test_help(self): + """ Test help flag """ + result = subprocess.run( + [WOLFSSL_BIN, "base64", "-h"], + capture_output=True, + timeout=60, + ) + self.assertEqual(result.returncode, 0, result.stderr + result.stdout) + self.assertGreater(len(result.stderr), 0, "out put was not completed") + + if __name__ == "__main__": test_main() diff --git a/tests/client/client-test.py b/tests/client/client-test.py index 0c329c34..4fbca71d 100644 --- a/tests/client/client-test.py +++ b/tests/client/client-test.py @@ -60,6 +60,12 @@ def test_s_client_x509(self): self.assertIn("-----BEGIN CERTIFICATE-----", result.stdout, "Expected x509 PEM output not found") + def test_client_help(self): + """ run help command for client """ + r = run_wolfssl("s_client", "-help") + self.assertEqual(r.returncode, 0, r.stderr) + self.assertIn("s_client" , r.stderr, "help menu was not printed") + class ShellInjectionTest(unittest.TestCase): """Regression tests for shell command injection via hostname. diff --git a/tests/dgst/dgst-test.py b/tests/dgst/dgst-test.py index 75325f2a..a7e2bfef 100644 --- a/tests/dgst/dgst-test.py +++ b/tests/dgst/dgst-test.py @@ -77,6 +77,38 @@ def test_fail_wrong_digest(self): os.path.join(CERTS_DIR, "server-key.der")) self.assertNotEqual(r.returncode, 0) + def test_help(self): + for flag in ("-help", "-h"): + r = run_wolfssl("dgst", flag) + self.assertEqual(r.returncode, 0, r.stderr) + self.assertIn("dgst", r.stdout + r.stderr) + + def test_sign_verify_all_hash_algs(self): + """Sign/verify round-trip for each supported hash algorithm. + + Covers the per-algorithm digest-selection branches in + clu_dgst_setup.c. -md5 is skipped under FIPS. + """ + algs = ["sha", "sha224", "sha256", "sha384", "sha512"] + if not is_fips(): + algs.append("md5") + input_file = os.path.join(CERTS_DIR, "server-key.der") + + for alg in algs: + with self.subTest(alg=alg): + sig_file = "dgst-{}.sig".format(alg) + self.addCleanup(lambda p=sig_file: os.remove(p) + if os.path.exists(p) else None) + r = run_wolfssl("dgst", "-" + alg, "-sign", + os.path.join(CERTS_DIR, "server-key.pem"), + "-out", sig_file, input_file) + self.assertEqual(r.returncode, 0, r.stderr) + + r = run_wolfssl("dgst", "-" + alg, "-verify", + os.path.join(CERTS_DIR, "server-keyPub.pem"), + "-signature", sig_file, input_file) + self.assertEqual(r.returncode, 0, r.stderr) + def test_dgst_out_roundtrip(self): """dgst -out creates the signature file; -signature round-trips.""" sig_file = "dgst-out-test.sig" diff --git a/tests/genkey_sign_ver/genkey-sign-ver-test.py b/tests/genkey_sign_ver/genkey-sign-ver-test.py index de424482..4f6e15ac 100644 --- a/tests/genkey_sign_ver/genkey-sign-ver-test.py +++ b/tests/genkey_sign_ver/genkey-sign-ver-test.py @@ -6,7 +6,7 @@ import unittest sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) -from wolfclu_test import WOLFSSL_BIN, run_wolfssl, test_main +from wolfclu_test import WOLFSSL_BIN, CERTS_DIR, run_wolfssl, test_main # Files that tests may create; cleaned up by tearDownClass _TEMP_FILES = [] @@ -385,12 +385,100 @@ def test_xmss_missing_height_value(self): self._track("xmss-bad.priv", "xmss-bad.pub") r = run_wolfssl("-genkey", "xmss", "-out", "xmss-bad", "-outform", "raw", "-output", "KEYPAIR", "-height") - self.assertNotEqual(r.returncode, 0, - "expected failure for missing -height value") + self.assertEqual(r.returncode, 0, + "expected defalut value of 20 set for -hieght no " + "crash") + + def test_xmss_missing_height_arg(self): + self._track("xmss-bad.priv", "xmss-bad.pub") + r = run_wolfssl("-genkey", "xmss", "-out", "xmss-bad", + "-outform", "raw", "-output", "KEYPAIR") + self.assertEqual(r.returncode, 0, + "expected defalut value for -hieght no " + "crash") + +@unittest.skipUnless(_has_algorithm("xmss"), "xmss not available") +class XmssmtTest(_GenkeySignVerifyBase): + + def test_xmssmt_raw(self): + # The XMSS^MT signer derives the parameter set from the key file name, + # so the keybase must be a valid param string with '-' in place of '/' + # (e.g. "XMSSMT-SHA2_20/2_256" -> "XMSSMT-SHA2_20-2_256"). -height 20 + # defaults to layer 2, matching this name. + keybase = "XMSSMT-SHA2_20-2_256" + self._track(keybase + ".priv", keybase + ".pub") + self._gen_sign_verify( + "xmssmt", keybase, "xmss-signed.sig", "raw", + extra_genkey_args=["-height", "20"], + skip_priv_verify=True, use_output_flag=True) + + def test_xmss_missing_height_value(self): + """-height with no value must fail gracefully (no crash).""" + self._track("xmss-bad.priv", "xmss-bad.pub") + r = run_wolfssl("-genkey", "xmssmt", "-out", "xmss-bad", + "-outform", "raw", "-output", "KEYPAIR", "-height") + self.assertEqual(r.returncode, 0, + "expected defalut value of 20 set for -hieght no " + "crash") + + def test_xmss_missing_height_arg(self): + self._track("xmss-bad.priv", "xmss-bad.pub") + r = run_wolfssl("-genkey", "xmssmt", "-out", "xmss-bad", + "-outform", "raw", "-output", "KEYPAIR") + self.assertEqual(r.returncode, 0, + "expected defalut value for -hieght no " + "crash") + + +class SignVerifySetupArgsTest(unittest.TestCase): + """Argument-parsing branches in clu_sign_verify_setup.c. + + These exercise the legacy `-rsa`/`-ecc`/... sign & verify entry point + (note the leading dash, which selects the legacy code path). + """ + + SIGN_FILE = "svsetup-sign-this.txt" + RSA_KEY = os.path.join(CERTS_DIR, "server-key.pem") + ECC_KEY = os.path.join(CERTS_DIR, "ecc-key.pem") + ECC_PUB = os.path.join(CERTS_DIR, "ecc-keyPub.pem") + + @classmethod + def setUpClass(cls): + config_log = os.path.join(".", "config.log") + if os.path.isfile(config_log): + with open(config_log, "r") as f: + if "disable-filesystem" in f.read(): + raise unittest.SkipTest("filesystem support disabled") + with open(cls.SIGN_FILE, "w") as f: + f.write("Sign this test data\n") + + @classmethod + def tearDownClass(cls): + _cleanup_files([cls.SIGN_FILE]) + + def test_sign_help(self): + r = run_wolfssl("-rsa", "-sign", "-help") self.assertGreaterEqual(r.returncode, 0, - "-height without value crashed with signal " + "sign help crashed with signal " "{}".format(r.returncode)) + self.assertIn("RSA Sign", r.stdout + r.stderr) + def test_verify_help(self): + r = run_wolfssl("-rsa", "-verify", "-help") + self.assertGreaterEqual(r.returncode, 0, + "verify help crashed with signal " + "{}".format(r.returncode)) + self.assertIn("RSA Verify", r.stdout + r.stderr) + + def test_generic_help(self): + """No -sign/-verify prints both the sign and verify help blocks.""" + r = run_wolfssl("-ecc", "-help") + self.assertGreaterEqual(r.returncode, 0, + "generic help crashed with signal " + "{}".format(r.returncode)) + combined = r.stdout + r.stderr + self.assertIn("ECC Sign", combined) + self.assertIn("ECC Verify", combined) class GenkeyArgvTest(unittest.TestCase): """Argument-bounds checks for the genkey subcommand entry point.""" diff --git a/tests/ocsp/ocsp-test.py b/tests/ocsp/ocsp-test.py index 9ad887b1..05873680 100644 --- a/tests/ocsp/ocsp-test.py +++ b/tests/ocsp/ocsp-test.py @@ -320,12 +320,34 @@ def test_12_graceful_shutdown(self): class TestWolfsslClientWolfsslResponder(_OCSPInteropBase): CLIENT_BIN = WOLFSSL_BIN RESPONDER_BIN = WOLFSSL_BIN + PORT = 3000 + + + def test_01_client_start_up(self): + """ successful round trip from client to server """ + resp = self._start_responder(INDEX_VALID, nrequest=1) + rc, out = _run_client(self.CLIENT_BIN, self.PORT, + ["-cert", os.path.join(CERTS_DIR, "server-cert.pem")]) + + resp.stop() + self.assertEqual(rc, 0, out) + self.assertIn("good", out.lower(), out) @unittest.skipUnless(HAS_OPENSSL, "openssl not available") class TestWolfsslClientOpensslResponder(_OCSPInteropBase): CLIENT_BIN = WOLFSSL_BIN RESPONDER_BIN = "openssl" + PORT = 3000 + + def test_01_client_start_up(self): + resp = self._start_responder(INDEX_VALID, nrequest=1) + rc, out = _run_client(self.CLIENT_BIN, self.PORT, + ["-cert", os.path.join(CERTS_DIR, "server-cert.pem")]) + + resp.stop() + self.assertEqual(rc, 0, out) + self.assertIn("good", out.lower(), out) @unittest.skipUnless(HAS_OPENSSL, "openssl not available") @@ -333,12 +355,32 @@ class TestOpensslClientWolfsslResponder(_OCSPInteropBase): CLIENT_BIN = "openssl" RESPONDER_BIN = WOLFSSL_BIN + PORT = 3000 + + def test_01_client_start_up(self): + resp = self._start_responder(INDEX_VALID, nrequest=1) + rc, out = _run_client(self.CLIENT_BIN, self.PORT, + ["-cert", os.path.join(CERTS_DIR, "server-cert.pem")]) + + resp.stop() + self.assertEqual(rc, 0, out) + self.assertIn("good", out.lower(), out) @unittest.skipUnless(HAS_OPENSSL, "openssl not available") class TestOpensslClientOpensslResponder(_OCSPInteropBase): CLIENT_BIN = "openssl" RESPONDER_BIN = "openssl" + PORT = 3000 + + def test_01_client_start_up(self): + resp = self._start_responder(INDEX_VALID, nrequest=1) + rc, out = _run_client(self.CLIENT_BIN, self.PORT, + ["-cert", os.path.join(CERTS_DIR, "server-cert.pem")]) + + resp.stop() + self.assertEqual(rc, 0, out) + self.assertIn("good", out.lower(), out) def load_tests(loader, tests, pattern): """Exclude the abstract _OCSPInteropBase from test discovery.""" diff --git a/tests/pkcs/pkcs12-test.py b/tests/pkcs/pkcs12-test.py index a7cfdbe5..7d067f1c 100644 --- a/tests/pkcs/pkcs12-test.py +++ b/tests/pkcs/pkcs12-test.py @@ -59,6 +59,32 @@ def test_pass_on_cmdline(self): "-passout", "pass:", "-in", P12_FILE) self.assertEqual(r.returncode, 0, r.stderr) + def test_help(self): + for flag in ("-help", "-h"): + r = run_wolfssl("pkcs12", flag) + self.assertEqual(r.returncode, 0, r.stderr) + self.assertIn("wolfssl pkcs12", r.stdout + r.stderr) + + def test_bad_argument_fails(self): + r = run_wolfssl("pkcs12", "-not-a-real-option", "-in", P12_FILE) + self.assertNotEqual(r.returncode, 0) + + def test_out_to_file(self): + out = "pkcs12-out.pem" + self.addCleanup(lambda: os.remove(out) if os.path.exists(out) else None) + r = run_wolfssl("pkcs12", "-nodes", "-passin", 'pass:wolfSSL test', + "-passout", "pass:", "-in", P12_FILE, "-out", out) + self.assertEqual(r.returncode, 0, r.stderr) + self.assertTrue(os.path.isfile(out), "pkcs12 -out did not create file") + with open(out, "r") as f: + self.assertIn("BEGIN ", f.read()) + + def test_out_bad_path_fails(self): + r = run_wolfssl("pkcs12", "-nodes", "-passin", 'pass:wolfSSL test', + "-passout", "pass:", "-in", P12_FILE, + "-out", os.path.join("no-such-dir", "out.pem")) + self.assertNotEqual(r.returncode, 0) + def test_nocerts_with_passout(self): r = subprocess.run( [WOLFSSL_BIN, "pkcs12", "-passin", "stdin", "-passout", "pass:", diff --git a/tests/pkcs/pkcs7-test.py b/tests/pkcs/pkcs7-test.py index bc82156e..b6baa472 100644 --- a/tests/pkcs/pkcs7-test.py +++ b/tests/pkcs/pkcs7-test.py @@ -53,6 +53,34 @@ def test_pem_to_der(self): ) self.assertEqual(r.returncode, 0, r.stderr) + def test_help(self): + for flag in ("-help", "-h"): + r = run_wolfssl("pkcs7", flag) + self.assertEqual(r.returncode, 0, r.stderr) + self.assertIn("wolfssl pkcs7", r.stdout + r.stderr) + + def test_bad_argument_shows_help(self): + r = run_wolfssl("pkcs7", "-not-a-real-option") + self.assertNotEqual(r.returncode, 0) + + def test_out_to_file(self): + out = "pkcs7-out.pem" + self.addCleanup(lambda: os.remove(out) if os.path.exists(out) else None) + + r = run_wolfssl("pkcs7", "-inform", "DER", + "-in", os.path.join(CERTS_DIR, "signed.p7b"), + "-outform", "PEM", "-out", out) + self.assertEqual(r.returncode, 0, r.stderr) + self.assertTrue(os.path.isfile(out), "pkcs7 -out did not create file") + with open(out, "r") as f: + self.assertIn("BEGIN PKCS7", f.read()) + + def test_out_bad_path_fails(self): + r = run_wolfssl("pkcs7", "-inform", "DER", + "-in", os.path.join(CERTS_DIR, "signed.p7b"), + "-out", os.path.join("no-such-dir", "out.pem")) + self.assertNotEqual(r.returncode, 0) + @unittest.skipIf(sys.platform == "win32", "binary DER stdin is unreliable on Windows") def test_stdin_input(self): diff --git a/tests/pkcs/pkcs8-test.py b/tests/pkcs/pkcs8-test.py index 287d2bae..2a23aa3d 100644 --- a/tests/pkcs/pkcs8-test.py +++ b/tests/pkcs/pkcs8-test.py @@ -72,6 +72,16 @@ def test_decrypt_and_convert(self): pkcs1_pem, shallow=False), "server-key.pem -traditional check failed") + def test_help(self): + for flag in ("-help", "-h"): + r = run_wolfssl("pkcs8", flag) + self.assertEqual(r.returncode, 0, r.stderr) + self.assertIn("wolfssl pkcs8", r.stdout + r.stderr) + + def test_bad_argument_fails(self): + r = run_wolfssl("pkcs8", "-not-a-real-option") + self.assertNotEqual(r.returncode, 0) + @unittest.skipIf(is_fips(), "skipped in FIPS builds") def test_stdin_input(self): pem_path = os.path.join(CERTS_DIR, "server-keyEnc.pem") diff --git a/tests/pkey/pkey-test.py b/tests/pkey/pkey-test.py index d8e50ce6..91b528d1 100644 --- a/tests/pkey/pkey-test.py +++ b/tests/pkey/pkey-test.py @@ -84,5 +84,29 @@ def test_pem_der_pem_public(self): self.assertEqual(r.stdout.strip(), ECC_PUBKEY_PEM) + def test_help(self): + for flag in ("-help", "-h"): + r = run_wolfssl("pkey", flag) + self.assertEqual(r.returncode, 0, r.stderr) + self.assertIn("wolfssl pkey", r.stdout + r.stderr) + + def test_pubout_from_private(self): + r = run_wolfssl("pkey", "-in", + os.path.join(CERTS_DIR, "ecc-key.pem"), "-pubout") + self.assertEqual(r.returncode, 0, r.stderr) + self.assertEqual(r.stdout.strip(), ECC_PUBKEY_PEM) + + def test_out_to_file(self): + out = "pkey-out.pem" + self._cleanup(out) + r = run_wolfssl("pkey", "-in", + os.path.join(CERTS_DIR, "ecc-key.pem"), + "-pubout", "-out", out) + self.assertEqual(r.returncode, 0, r.stderr) + self.assertTrue(os.path.isfile(out), "pkey -out did not create file") + with open(out, "r") as f: + self.assertIn("BEGIN PUBLIC KEY", f.read()) + + if __name__ == "__main__": test_main() diff --git a/tests/pkey/rsa-test.py b/tests/pkey/rsa-test.py index fa393be3..3e58b275 100644 --- a/tests/pkey/rsa-test.py +++ b/tests/pkey/rsa-test.py @@ -167,5 +167,18 @@ def test_invalid_outform_error_message(self): combined[:200])) + def test_help(self): + for flag in ("-help", "-h"): + r = run_wolfssl("rsa", flag) + self.assertEqual(r.returncode, 0, r.stderr) + self.assertIn("wolfssl rsa", r.stdout + r.stderr) + + def test_pubout_from_private(self): + r = run_wolfssl("rsa", "-in", + os.path.join(CERTS_DIR, "server-key.pem"), "-pubout") + self.assertEqual(r.returncode, 0, r.stderr) + self.assertEqual(r.stdout.strip(), RSA_PUBKEY_PEM) + + if __name__ == "__main__": test_main() diff --git a/tests/server/server-test.py b/tests/server/server-test.py index 54eae670..6a6b105d 100644 --- a/tests/server/server-test.py +++ b/tests/server/server-test.py @@ -24,8 +24,23 @@ def setUpClass(cls): if "disable-filesystem" in f.read(): raise unittest.SkipTest("filesystem support disabled") + def test_help(self): + """s_server -help prints usage and exits cleanly.""" + for flag in ("-help", "-h"): + r = subprocess.run( + [WOLFSSL_BIN, "s_server", flag], + capture_output=True, text=True, stdin=subprocess.DEVNULL, + timeout=30, + ) + self.assertEqual(r.returncode, 0, r.stderr) + self.assertIn("s_server", r.stdout + r.stderr) + def test_server_client(self): - """Start s_server, connect with s_client, verify handshake.""" + """Start s_server, connect with s_client, verify handshake. + + Exercises the -CAfile, -version, -naccept and -www argument-parsing + branches in clu_server_setup.c in addition to the basic handshake. + """ readyfile = "readyfile" if os.path.exists(readyfile): os.remove(readyfile) @@ -35,6 +50,8 @@ def test_server_client(self): [WOLFSSL_BIN, "s_server", "-port", "11111", "-key", os.path.join(CERTS_DIR, "server-key.pem"), "-cert", os.path.join(CERTS_DIR, "server-cert.pem"), + "-CAfile", os.path.join(CERTS_DIR, "ca-cert.pem"), + "-version", "3", "-naccept", "1", "-www", "-noVerify", "-readyFile", readyfile], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL, diff --git a/tests/x509/x509-req-test.py b/tests/x509/x509-req-test.py index b9d1ec6d..a18282ba 100644 --- a/tests/x509/x509-req-test.py +++ b/tests/x509/x509-req-test.py @@ -143,6 +143,44 @@ def test_req_new_with_subj(self): "Got: {!r}".format(subject_line)) + def test_req_new_interactive_name(self): + """req -new with no -subj/-config reads the name fields from stdin. + + Exercises wolfCLU_CreateX509Name(). The prompts consume one line each: + Country, State, Locality, Organization, Org-Unit, Common Name, Email. + """ + tmp = _tmp("test_req_interactive.csr") + self._clean(tmp) + name_input = ("US\nMontana\nBozeman\nwolfSSL\n" + "engineering\nexample.com\ntest@example.com\n") + r = subprocess.run( + [WOLFSSL_BIN, "req", "-new", + "-key", os.path.join(CERTS_DIR, "server-key.pem"), + "-out", tmp], + input=name_input, capture_output=True, text=True, timeout=60) + self.assertEqual(r.returncode, 0, r.stderr) + self.assertTrue(os.path.isfile(tmp), "req did not create CSR") + + r2 = subprocess.run( + [WOLFSSL_BIN, "req", "-in", tmp, "-text", "-verify"], + input=name_input, capture_output=True, text=True, timeout=60) + self.assertEqual(r2.returncode, 0, r2.stderr) + self.assertIn("example.com", r2.stdout) + + def test_req_new_interactive_skip_fields(self): + """Empty lines skip the corresponding name fields (still succeeds).""" + tmp = _tmp("test_req_interactive_skip.csr") + self._clean(tmp) + # Only a common name; everything else left blank. + name_input = "\n\n\n\n\nexample.com\n\n" + r = subprocess.run( + [WOLFSSL_BIN, "req", "-new", + "-key", os.path.join(CERTS_DIR, "server-key.pem"), + "-out", tmp], + input=name_input, capture_output=True, text=True, timeout=60) + self.assertEqual(r.returncode, 0, r.stderr) + self.assertTrue(os.path.isfile(tmp), "req did not create CSR") + def test_req_with_prompt_config(self): """req with prompt config file creates CSR with SAN.""" tmp_csr = _tmp("test_req_prompt.csr") diff --git a/tests/x509/x509-verify-test.py b/tests/x509/x509-verify-test.py index 4f42be6c..a8c5763f 100644 --- a/tests/x509/x509-verify-test.py +++ b/tests/x509/x509-verify-test.py @@ -73,6 +73,28 @@ def test_verify_self_as_ca_fails(self): os.path.join(CERTS_DIR, "server-cert.pem")) self.assertNotEqual(r.returncode, 0) + def test_help_trailing_h(self): + """verify -h (as the final argument) prints usage and exits 0.""" + r = run_wolfssl("verify", "-h") + self.assertEqual(r.returncode, 0, r.stderr) + self.assertIn("wolfssl verify", r.stdout + r.stderr) + + def test_help_flag(self): + """verify -help prints usage and exits 0.""" + r = run_wolfssl("verify", "-help", + os.path.join(CERTS_DIR, "server-cert.pem")) + self.assertEqual(r.returncode, 0, r.stderr) + self.assertIn("wolfssl verify", r.stdout + r.stderr) + + def test_verify_der_cert(self): + """A DER-encoded cert is loaded via the DER fallback path.""" + der_cert = os.path.join(CERTS_DIR, "ca-cert.der") + if not os.path.isfile(der_cert): + self.skipTest("ca-cert.der not present") + r = run_wolfssl("verify", "-partial_chain", "-CAfile", + os.path.join(CERTS_DIR, "ca-cert.pem"), der_cert) + self.assertEqual(r.returncode, 0, r.stderr) + def test_verify_partial_chain(self): """verify with -partial_chain allows self as CA.""" r = run_wolfssl("verify", "-partial_chain", "-CAfile",