Conversation
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The security fixes are coherent across implementation, configuration, documentation, and regression tests, with no unresolved correctness issues found.
Review effort: Balanced
Findings: None
What changed in this PR
This PR addresses Fenrir findings across SCEP validation/error handling, server configuration, and CLI credential hygiene.
Changes:
- Enforces valid SCEP envelopes, attributes, transaction IDs, and AES-128-CBC support.
- Rejects invalid bind addresses and non-EST post-handshake authentication.
- Scrubs CLI secrets from process arguments and adds regression coverage.
| File | Description |
|---|---|
wolfcert/server.h |
Documents bind-address and PHA restrictions. |
wolfcert/scep.h |
Documents transaction ID validation. |
wolfcert/check_config.h |
Enforces AES-128-CBC for SCEP builds. |
tests/unit/test_server_ca_store.c |
Tests invalid bind hosts. |
tests/integration/test_scep_roundtrip.c |
Tests nonce lengths and issuance failures. |
tests/integration/test_scep_poll_roundtrip.c |
Tests malformed polls and transaction IDs. |
tests/integration/test_scep_async_roundtrip.c |
Tests session transaction ID validation. |
tests/integration/test_est_pha_roundtrip.c |
Tests PHA protocol scoping. |
tests/integration/cli_proto_scoping.sh |
Tests argv secret scrubbing. |
tests/CMakeLists.txt |
Adds internal headers to the poll test. |
src/server.c |
Validates bind hosts and PHA usage. |
src/scep/scep_server.c |
Tightens request validation and signed failure replies. |
src/scep/scep_client.c |
Validates transaction IDs and requires AES support. |
src/internal.h |
Removes the obsolete copied bind host. |
Makefile.am |
Adds the poll test's internal include path. |
docs/ARCHITECTURE.md |
Documents EST-only PHA behavior. |
configure.ac |
Adds the Autoconf AES capability gate. |
CMakeLists.txt |
Adds the CMake AES capability gate. |
cli/wolfcert_server.c |
Scrubs server secrets and clarifies listen syntax. |
cli/wolfcert_client.c |
Copies, scrubs, and securely frees client secrets. |
CLAUDE.md |
Documents the SCEP AES requirement. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
@wolfSSL-Fenrir-bot review balanced |
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #46
Scan targets checked: wolfcert-src, wolfcert-bugs
Coverage: 9 of 12 in-scope changed file(s) opened by the reviewer; not opened: tests/unit/test_server_ca_store.c, wolfcert/scep.h, wolfcert/server.h
Fenrir result: Approved ✅
No new issues found in the changed files.
Advisory only — this automated result does not count as a GitHub approval.
Review tier: Balanced
yosuke-wolfssl
left a comment
There was a problem hiding this comment.
Most of the fixes look right to me. The branch builds without warnings, and ctest passes 30/30 on macOS.
Main points, inline below:
- GetCertInitial (F-11063): a poll still finds its parked request by transactionID alone. Requiring the envelope stops a poll with absent or garbage content, but not a valid envelope signed by a different key.
parse_basic(): a failed copy starts the server with Basic auth weakened or off.--challengealready exits on the same failure.- PrintableString: the new check covers one client path;
enc_printable_n()writes the 0x13 tag for every attribute. - Issuance failure on a poll: the case the commit message names is untested, and SCEP parks a CSR without checking its signature.
- AES-128-CBC requirement: no negative-configure case, the user-facing docs do not mention it, and
WOLFCERT_NO_WOLFSSL_FEATURE_CHECKnow skips it with no fallback. bind_hosttest: it cannot see the CA-persist ordering the commit message describes.- Nits: a bare
16, a stale test comment, a long line.
Not on diff lines:
- The
HAVE_AES_CBC/WOLFSSL_AES_128guards intests/integration/test_scep_roundtrip.c(lines 424, 1067, 1419, 1510, 1586, 1638) andtests/unit/test_scep_msg.c(1954) are now always true in a SCEP build. So is the reason given attest_scep_roundtrip.c:422, "only exist when wolfSSL can supply one". wolfcert_server_free()freescfg_basic_passandcfg_challengewithout wiping them. With the CLIs now wiping their own copies, those are the copies left in the heap.wolfcert-serverreports a rejected--listenhost only as "start failed (Bad argument)"; thebind_hostmessage reaches stderr only withWOLFCERT_LOGset.--listen localhost:PORTused to start, so the error deserves a hint.- The
--tls-post-handshake-authhelp text still lists only the TLS requirements, not--proto est. - Test cases worth adding:
- a GetCertInitial with a valid envelope, signed by a different key
- a bad-signature CSR parked on the approval server, then polled
- a transactionID using the allowed punctuation (
-,:,.), and one with@or a byte above 0x7F - a start with
bind_host = NULL
| if (rc != WOLFCERT_OK && strcmp(mt, "20") != 0) { | ||
| /* Decryption matters for 19/17 (CSR inside); for 20 the payload | ||
| * is IssuerAndSubject which the server matches by txid anyway. */ | ||
| if (rc != WOLFCERT_OK) { |
There was a problem hiding this comment.
This stops a poll whose content is absent or is not an envelope. handle_get_cert_initial() still finds the entry with pending_find(p, tid, tid_len) and never looks at the poll's signer, and anyone can build a valid envelope because the CA certificate is public.
I checked it on this branch. After the PKCSReq in poll_path(), a wolfcert_scep_get_cert_initial() with r1.transaction_id, a fresh RSA key and that key's own CSR drains the entry and issues. The caller cannot decrypt the reply, which is enveloped to the stored signer, but the real poll at test_scep_poll_roundtrip.c:203 then gets badCertId.
handle_enroll() already has the rule. Passing signer_cert into handle_get_cert_initial() and checking it before pending_remove() would tie the poll to its request:
if (signer_cert == NULL ||
signer_matches_csr(signer_cert, signer_cert_len,
e->csr_der, e->csr_len, s->heap) != WOLFCERT_OK)
return send_pki_failure(s, fd, tid, tid_len, snonce, snonce_len,
"4" /* badCertId */);On a mismatch the entry stays queued. That poll would make a good third case next to the two unenveloped ones.
There was a problem hiding this comment.
Fixed in c0f64f6: the poll's signer must match the parked CSR's key (signer_matches_csr()), else badCertId and the entry stays. Your foreign-key poll is now a test case.
| free(*user); | ||
| free_secret(*pass); | ||
| *user = strndup(arg, (size_t)(colon - arg)); | ||
| *pass = strdup(colon + 1); |
There was a problem hiding this comment.
If either copy fails, the server still starts. A NULL user makes check_basic_auth() admit every request, and a NULL pass makes user: the accepted credential. The --challenge case exits on the same failure, so the two now disagree:
*user = strndup(arg, (size_t)(colon - arg));
*pass = strdup(colon + 1);
wc_ForceZero(colon + 1, (word32)strlen(colon + 1));
if (*user == NULL || *pass == NULL)
return -1;There was a problem hiding this comment.
Fixed in f11ae82: a failed copy of either half exits, like --challenge.
|
|
||
| /* X.680 PrintableString repertoire, which the transactionID attribute is typed | ||
| * as (RFC 8894 section 3.2.1.1). */ | ||
| static int scep_is_printable_string(const uint8_t* s, size_t len) |
There was a problem hiding this comment.
Nothing in wolfCert checked the PrintableString characters before this, and wolfSSL has no public helper for it, so the check is needed. But it guards one caller, and enc_printable_n() in src/scep/scep_msg.c is where every 0x13 attribute is written. Checking there would also cover the test server, which copies the request's transactionID into its CertRep unchecked. wolfcert_scep_build_pki_message() runs before the POST, so the rejection would still come before any network I/O, as long as it maps to WOLFCERT_ERR_BAD_ARG.
The CSR subject has the same gap, outside this PR: wolfcert_csr_build() with C=U_,serialNumber=ab_c@1 writes both as PrintableString. wolfSSL defaults countryEnc and serialDevEnc to CTC_PRINTABLE, and parse_subject_dn() checks only length. One helper in src/internal.h could serve both.
There was a problem hiding this comment.
Moved in d96b045: enc_printable_n() now checks every 0x13 attribute via wolfcert_is_printable_string() in internal.c, returning WOLFCERT_ERR_BAD_ARG before the POST. The test server rejects a bad transactionID with 400. Added the punctuation, @ and 0x80 cases. CSR subject is noted for a follow-up; the helper is ready for it.
|
|
||
| /* Closed by the non-OK return; the flag is for the header. */ | ||
| s->keep_alive = 0; | ||
| send_rc = send_pki_failure(s, fd, tid, tid_len, snonce, snonce_len, |
There was a problem hiding this comment.
Two things on the poll path:
- Untested.
check_bad_csr_sig()runs against the server without approval, so it reaches this branch only throughhandle_enroll(). A bad-signature CSR parked on the approval server intest_scep_poll_roundtrip.c, then polled, would cover the case the commit message names. The first poll should get FAILURE/badRequest, and a second poll badCertId. - Parked without checking. That case exists because
handle_enroll()parks a CSR without checking its signature, so the client is told PENDING for a request that cannot succeed. Various hardening and fixes #45 makes the EST server verify a CSR before parking it. Doing the same here would refuse it on the PKCSReq instead.
There was a problem hiding this comment.
Went with verifying before parking (5d8a236): a bad-signature CSR now gets FAILURE/badRequest on the PKCSReq. The poll test checks that, plus badCertId on a later poll since nothing was parked.
| WolfCertServerCfgSrv cfg; | ||
| WolfCertServer* srv = NULL; | ||
|
|
||
| ca_store_cfg(&cfg, NULL); |
There was a problem hiding this comment.
With no store configured, this checks only the return code. If the inet_pton() check moved back below CA generation, this test would still pass. The commit message's claim, that a bad address no longer generates and persists a CA, needs a store here and a check that nothing was written to it.
Every server in the suite sets bind_host, so the NULL branch, now the only way to bind all interfaces, is never run. One start with bind_host = NULL would cover it.
There was a problem hiding this comment.
Done in 05403a4: bad addresses now run against a memory store and assert nothing was written (verified by moving the check below CA generation), plus a bind_host = NULL start.
| if (tid == NULL || tid_len == 0 || snonce == NULL || snonce_len == 0 || | ||
| /* RFC 8894 section 3.2.1 requires all three in every message, with a | ||
| * 16-byte senderNonce, or no CertRep could answer it. */ | ||
| if (tid == NULL || tid_len == 0 || snonce == NULL || snonce_len != 16 || |
There was a problem hiding this comment.
Nit: scep_client.c names this SCEP_NONCE_SZ. Moving that into src/internal.h and using it here would tie the server's check to the size the client generates.
There was a problem hiding this comment.
Done, SCEP_NONCE_SZ is in internal.h now (3f4f883).
| memset(snonce, 0x22, sizeof(snonce)); | ||
| memset(snonce_long, 0x33, sizeof(snonce_long)); | ||
|
|
||
| /* Each round omits one required attribute; the last is the control that |
There was a problem hiding this comment.
Rounds 6 and 7 send every attribute, with a senderNonce of the wrong length, so "omits one required attribute" no longer holds. Perhaps:
/* Each round omits or mis-sizes one required attribute; the last is the
* control that proves this raw-POST harness reaches the issuance path. */| /* RFC 8894 section 2.9 makes AES128-CBC mandatory-to-implement for SCEP. */ | ||
| #if defined(WOLFCERT_HAVE_SCEP) && (!defined(WOLFSSL_AES_128) || \ | ||
| !defined(HAVE_AES_CBC) || !defined(HAVE_AES_DECRYPT)) | ||
| #error "wolfCert SCEP requires AES-128-CBC (RFC 8894). Rebuild wolfSSL without NO_AES_128, NO_AES_CBC or NO_AES_DECRYPT, or drop WOLFCERT_HAVE_SCEP." |
There was a problem hiding this comment.
- No negative-configure case.
scripts/ci/assert-configure-fails.shhasno-rsa-scepfor the RSA rule. A matching row for a wolfSSL built with-DNO_AES_128would show that both build systems keep rejecting it. - Docs. Only
CLAUDE.mdrecords the requirement.docs/EMBEDDED.md:38still lists just "AES", and neitherexamples/user_settings.h.examplenor theWOLFCERT_SCEPhelp inzephyr/Kconfigmentions it. Embedded builds are the ones most likely to drop AES-128. - Opt-out. This
#errorsits inside theWOLFCERT_NO_WOLFSSL_FEATURE_CHECKblock, and the source guards it replaces are gone. An opted-out build without AES-128 now compiles; the client then fails inside the PKCS#7 encoder, and the test server advertisesAESandSCEPStandard. Is that intended, or should this check sit outside the opt-out like the RSA rule at line 71?
There was a problem hiding this comment.
Added the no-aes128-scep negative case (both build systems reject it) and documented the requirement in EMBEDDED.md, user_settings.h.example and the Zephyr Kconfig (b1f26d8). The opt-out is intended: it behaves like the NO_AES check above it, and in Tier 1 it would misfire whenever wolfSSL's options are unresolved.
| * checked against `tls_client_ca_pem`, which it requires. | ||
| * wolfcert_server_start() returns WOLFCERT_ERR_UNSUPPORTED without | ||
| * checked against `tls_client_ca_pem`, which it requires. Other protocols | ||
| * reject it with WOLFCERT_ERR_BAD_ARG. wolfcert_server_start() returns WOLFCERT_ERR_UNSUPPORTED without |
There was a problem hiding this comment.
Nit: this line runs to about 105 columns, where the rest of the header wraps at 80. docs/ARCHITECTURE.md:169 has the same issue.
There was a problem hiding this comment.
Wrapped, along with ARCHITECTURE.md (fbc2f24).
The test server ignored a de-enveloping failure for messageType 20 and dispatched the poll anyway, matching the pending entry by transactionID alone. A signed GetCertInitial with absent or non-envelope content could therefore drain a parked request and trigger issuance. Route every PKIOperation whose envelope does not decrypt to the CA through the signed FAILURE CertRep path, as RFC 8894 requires the pkcsPKIEnvelope for all of them. The envelope alone does not bind the poll to its request: the CA certificate is public, so anyone can build one. The poll's signer must now carry the same public key as the parked CSR, the rule PKCSReq already enforces. A mismatch gets FAILURE/badCertId and leaves the entry queued. The poll roundtrip test now posts an unenveloped, a malformed and a validly enveloped GetCertInitial signed by a different key for a pending transaction, expects each to be refused, and checks the legitimate poll still succeeds afterwards. Fixes F-11063.
The server parses the CSR without verification for its SPKI and challenge checks, so a CSR with a broken self-signature is only refused inside wolfcert_ca_issue. That branch replied with a plain HTTP 400, leaving the client without a signed CertRep, its transactionID, recipientNonce or failInfo, and surfacing a PKI rejection as a transport error. Reply with pkiStatus FAILURE and failInfo badRequest instead and close the connection, matching the other rejection paths. The integration test enrolls with a CSR whose signature has one bit flipped and expects the FAILURE CertRep. A server that requires approval used to park such a CSR and answer PENDING for a request that could never issue. It now verifies the CSR signature before parking and refuses it on the PKCSReq with the same FAILURE/badRequest, as the EST server does. The poll test sends a bad-signature PKCSReq to the approval server, expects FAILURE, and then a poll for its transactionID gets badCertId since nothing was parked. Fixes F-12110.
RFC 8894 section 2.9 makes AES128-CBC mandatory-to-implement, but the build only rejected NO_AES. A wolfSSL built with NO_AES_128, NO_AES_CBC or NO_AES_DECRYPT still configured with SCEP enabled. The client then refused an explicit AES-128 request and, in AUTO mode, sent 3DES even to a CA advertising AES, while the test server silently dropped to 3DES and stopped advertising SCEPStandard. Reject such a wolfSSL whenever SCEP is enabled, in check_config.h, configure.ac and CMake alike, and drop the AES-128 guards from the client cipher selection and the 3DES branch from the server. The 3DES fallback for a CA that does not advertise AES is unchanged. The check_config.h rule sits with the other wolfSSL feature checks, so WOLFCERT_NO_WOLFSSL_FEATURE_CHECK skips it as it skips NO_AES. A new neg-no-aes128 wolfSSL config and no-aes128-scep case in assert-configure-fails.sh keep both build systems rejecting it. EMBEDDED.md, user_settings.h.example and the Zephyr WOLFCERT_SCEP help state the requirement, and the SCEP tests lose the AES-128 guards that are now always true. Fixes F-12894.
RFC 8894 types the transactionID attribute as a PrintableString, and the signed-attribute encoder tags it 0x13 without looking at its bytes. The GetCertInitial entry points accepted any caller-supplied byte sequence, so a value holding characters such as '_', control bytes or NUL went on the wire as a malformed PrintableString. The test server likewise copied a request's transactionID into its CertRep unchecked. The IDs the library generates are hex and always conform, so only an inherited or peer-supplied ID can trip this. Check the repertoire in enc_printable_n, which writes every 0x13 attribute (messageType, transactionID, pkiStatus, failInfo), and return WOLFCERT_ERR_BAD_ARG from wolfcert_scep_build_pki_message. That runs before the POST, so the one-shot and both session GetCertInitial paths still fail before any network I/O. The check lives in wolfcert_is_printable_string in internal.c so the CSR subject can reuse it. The test server rejects a non-PrintableString transactionID with HTTP 400 alongside the other required-attribute checks. The existing long-ID poll tests filled their buffers with 0x11 and now use 'A', and the unit and raw-POST tests that used binary transactionIDs now use hex digits. New cases cover '_', '@', 0x11, 0x80 and NUL on the one-shot API, '_' on the blocking session API, and a transactionID made of the allowed punctuation, which reaches the server. Fixes F-12895.
The test server checked only that a PKIOperation carried a non-empty senderNonce, and the parser keeps whatever length the request supplied. Every dispatch path hands that nonce to send_cert_rep, which echoes it as the CertRep recipientNonce, so a signed request with a short or long senderNonce was processed, possibly issued, and answered with a malformed recipientNonce. RFC 8894 section 3.2.1 fixes the senderNonce at 16 bytes. Extend the required-attribute check in handle_pki_op to reject any senderNonce whose length is not 16 with HTTP 400, ahead of de-enveloping, issuance and any pending-queue change. check_required_attrs in the SCEP roundtrip test now posts an 8-byte and a 17-byte senderNonce and expects both to be rejected. Fixes F-12896.
wolfcert_server_start fell back to INADDR_ANY whenever inet_pton could not parse bind_host, so a hostname such as localhost, an IPv6 literal, an empty string or a mistyped address made the server listen on every IPv4 interface instead of the one the operator asked for. The CLI passes --listen through unchanged, so --listen localhost:8080 exposed the enrollment endpoint externally. Parse bind_host before any allocation and return WOLFCERT_ERR_BAD_ARG when it is not a numeric IPv4 address. Only a NULL bind_host now binds all interfaces. Checking up front also stops a bad address from generating and persisting a CA before the start fails. The wolfcert-server --listen help now says the address must be numeric IPv4, and a failed start prints the library's error detail, so --listen localhost:PORT names the reason instead of only "Bad argument". The unit test starts each bad address against a memory store and checks no CA was written to it, and starts once with a NULL bind_host. cli_proto_scoping.sh checks the refusal reason reaches stderr. Fixes F-9813.
With tls_post_handshake_auth set, tls_setup drops WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT so the handshake can complete anonymously and the client certificate is requested later. Only the EST enrollment handler ever requests and checks that certificate. A SCEP server started with a client CA and PHA enabled therefore served clients that never presented a certificate, silently turning the configured mutual TLS into optional client auth. wolfcert_server_start now rejects tls_post_handshake_auth with WOLFCERT_ERR_BAD_ARG for any protocol other than EST. The header, ARCHITECTURE.md and the wolfcert-server --tls-post-handshake-auth help document the restriction. Fixes F-11068.
wolfcert-server took the EST Basic password and the SCEP challenge password on the command line and left both in argv for the life of the process. The challenge was used directly as the optarg pointer, and parse_basic copied the password without touching the original argument, so any local user able to read process arguments through ps or /proc/<pid>/cmdline could recover them. parse_basic now zeroes the password half of its argument after copying it, and --challenge is copied with strdup before its optarg is zeroed. Both copies are wiped before they are freed. A failed copy of either option exits instead of starting the server without it: a NULL Basic user would have turned Basic auth off, and a NULL password would have accepted "user:". wolfcert_server_free also wipes the library's own copies of the Basic password and the challenge before freeing them. A new group in cli_proto_scoping.sh starts the server with both options and checks that neither secret appears in ps output once it is listening. Fixes F-12911.
wolfcert-client kept --pass and --challenge as direct optarg pointers, so the EST Basic password and the SCEP challenge password stayed in argv for the whole run. Enrollment with SCEP pending polling or an EST Retry-After wait can keep the client alive for minutes, during which any local user able to read process arguments through ps or /proc/<pid>/cmdline could recover them. parse_common now copies both values through a new opt_secret helper, which zeroes the argv original and wipes any earlier copy when the flag is repeated. opts_free wipes and frees the copies. A new group in cli_proto_scoping.sh parks the client on a --trust FIFO after option parsing and checks that neither secret appears in its ps output. Fixes F-12912.
Problem
A batch of Fenrir findings against the SCEP client and test server, the server's bind and TLS configuration, and the two CLIs:
wolfcert_ca_issue, and that branch answered with a plain HTTP 400. The client got no signed CertRep, transactionID, recipientNonce or failInfo, and saw a PKI rejection as a transport error.NO_AES_128,NO_AES_CBCorNO_AES_DECRYPTstill configured with SCEP on. The client then refused an explicit AES-128 request and in AUTO mode sent 3DES to a CA advertising AES, and the server silently dropped to 3DES._, control bytes and NUL.wolfcert_server_startfell back toINADDR_ANYwheneverinet_ptonrejectedbind_host, so--listen localhost:8080, an IPv6 literal or a typo bound every IPv4 interface.tls_post_handshake_authset, the handshake completes without a client certificate and only the EST enroll handler requests one later. A SCEP server with a client CA and PHA on therefore served clients that never presented a certificate.wolfcert-serverkept the Basic password and--challenge, andwolfcert-clientkept--passand--challenge, in argv for the life of the process, readable throughpsor/proc/<pid>/cmdline.Fix
scep: require a pkcsPKIEnvelope on GetCertInitial: every PKIOperation whose envelope does not decrypt to the CA now takes the signed FAILURE CertRep path, polls included.scep: answer a CA issuance failure with a CertRep FAILURE: the server replies with pkiStatus FAILURE and failInfo badRequest and closes the connection, for both PKCSReq/RenewalReq and the first poll of a parked request.scep: require AES-128-CBC for SCEP builds:check_config.h,configure.acand CMake all reject such a wolfSSL when SCEP is enabled. The AES-128 guards in the client cipher selection and the server's 3DES branch are gone. The client's 3DES fallback for a CA that does not advertise AES is unchanged.scep: reject a non-PrintableString transactionID on GetCertInitial:scep_build_txid, which the one-shot and both session paths share, validates an inherited ID and returnsWOLFCERT_ERR_BAD_ARGbefore any network I/O. Generated IDs are hex and unaffected.scep: reject a senderNonce that is not 16 bytes:handle_pki_opanswers any other length with HTTP 400, ahead of de-enveloping, issuance and any pending-queue change.Reject a bind_host that is not a numeric IPv4 address:bind_hostis parsed before any allocation, so a bad address also no longer generates and persists a CA before the start fails. Only a NULLbind_hostbinds all interfaces. The unusedcfg_bind_hostcopy is gone, and the--listenhelp saysADDR:PORT.Reject post-handshake auth for non-EST server protocols:wolfcert_server_startreturnsWOLFCERT_ERR_BAD_ARGfor PHA with any protocol other than EST.Scrub server CLI secrets from argv after parsing:parse_basiczeroes the password half of its argument after copying it, and--challengeis copied before its optarg is zeroed. Both copies are wiped before they are freed, including when a flag is repeated, and a failed copy of--challengeexits instead of starting the server without the challenge check.Scrub client CLI credentials from argv after parsing: a newopt_secret()helper copies--passand--challenge, zeroes the argv original and wipes any earlier copy on a repeat.opts_free()wipes and frees both.Closes F-9813, F-11063, F-11068, F-12110, F-12894, F-12895, F-12896, F-12911, F-12912.
Behaviour changes for callers
NO_AES_128,NO_AES_CBCorNO_AES_DECRYPTnow fails configure and thecheck_config.hgate when SCEP is enabled. Build with--disable-scep(CMake-DWOLFCERT_ENABLE_SCEP=OFF) to keep using such a wolfSSL.WolfCertServerCfgSrv.bind_hostmust be NULL or a numeric IPv4 address. A hostname or IPv6 literal now fails withWOLFCERT_ERR_BAD_ARGinstead of binding every interface, and so doeswolfcert-server --listen localhost:PORT.tls_post_handshake_authis accepted only withWOLFCERT_PROTO_EST.wolfcert_scep_get_cert_initial()and the session GetCertInitial calls returnWOLFCERT_ERR_BAD_ARGfor a transactionID outside the PrintableString repertoire.Tests
scep_poll_roundtrip: an unenveloped and a malformed GetCertInitial for a pending transaction both get FAILURE/badRequest, and the real poll still succeeds afterwards. The long-ID poll case now fills withAinstead of 0x11, and new cases refuse a transactionID holding_, 0x11 or NUL on the one-shot API.scep_roundtrip: a CSR with one signature bit flipped gets a FAILURE CertRep.check_required_attrsposts an 8-byte and a 17-byte senderNonce and expects both to be rejected.scep_async_roundtrip: the long-ID case fills withAinstead of 0x11, and a transactionID holding_is refused on the blocking session API.server_ca_storeunit:localhost,::1, an empty string,127.0.0.300and an address with a trailing space are each refused withWOLFCERT_ERR_BAD_ARG.est_pha_roundtrip: a SCEP server with PHA enabled fails to start.cli_proto_scoping.sh: a server started with--basicand--challenge, and a client parked on a--trustFIFO after option parsing, show neither secret inpsoutput.