SCEP gap-filling#7
Conversation
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #7
Scan targets checked: wolfcert-bugs, wolfcert-src
Findings: 2
2 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Findings are non-blocking.
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #7
Scan targets checked: wolfcert-bugs, wolfcert-src
Findings: 4
4 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Findings are non-blocking.
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #7
Scan targets checked: wolfcert-bugs, wolfcert-src
Findings: 3
3 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Findings are non-blocking.
The renewal entry points took a new_key argument that was ignored ((void)new_key): the renewed public key is already carried in the CSR and the pkiMessage is signed with current_key. Remove the dead parameter from wolfcert_scep_renewal_req and _renewal_req_ex. Also parse the RFC 8894 section 3.2.1.4 failInfo attribute out of a FAILURE CertRep and populate WolfCertScepResult.fail_info, which was previously always left at -1. wolfcert_scep_parse_pki_message gains an out_fail_info output; all non-consuming callers pass NULL. The SCEP poll roundtrip test now asserts the client surfaces failInfo=4 (badCertId) for an unknown transaction.
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #7
Scan targets checked: wolfcert-bugs, wolfcert-src
Findings: 2
2 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Findings are non-blocking.
Add a public helper to check a GetCACert response against a fingerprint obtained out of band before it is trusted as a CA anchor. It hashes the whole DER certificate with SHA-256 (or SHA-1 / SHA-512) and compares in constant time; WOLFCERT_SCEP_FP_AUTO selects the digest from the expected length. Returns WOLFCERT_ERR_AUTH on mismatch, WOLFCERT_ERR_UNSUPPORTED when the digest is not compiled into wolfSSL. MD5 is intentionally not offered. Unit-tested in test_scep_msg for match, single-bit tamper, AUTO dispatch, and length/argument misuse.
|
Requires wolfSSL/wolfssl#10928 |
There was a problem hiding this comment.
Pull request overview
This PR closes multiple RFC 8894 SCEP conformance/robustness gaps across the client, in-tree SCEP test server, and test/CI coverage. It adds a GET PKIOperation fallback, a CA-fingerprint trust-bootstrap helper, surfaces failInfo on failures, adopts wc_ConstantCompare, and updates CI/interop/docs accordingly.
Changes:
- Add SCEP CA-fingerprint trust-bootstrap API (
wolfcert_scep_verify_ca_fingerprint) and surface CertRepfailInfo. - Implement RFC 8894 §4.1 HTTP GET
PKIOperationfallback (client + server) with new tests for negative/malformed branches. - Harden/configure ecosystem updates:
wc_ConstantCompareusage + config gate, DES3 enabled in CI, interop script made strict, docs updated.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
wolfcert/scep.h |
Adds fingerprint API + enum; removes unused renewal new_key parameter; documents failInfo field. |
wolfcert/check_config.h |
Adds compile-time check for WOLFSSL_NO_CONST_CMP (requires wc_ConstantCompare). |
src/scep/scep_client.c |
Implements fingerprint verification helper, GET PKIOperation URL builder + GET fallback, parses/surfaces failInfo, removes renewal new_key. |
src/scep/scep_msg.c |
Extends wolfcert_scep_parse_pki_message to optionally extract failInfo. |
src/scep/scep_server.c |
Moves request buffer to heap; adds GET PKIOperation decoder/dispatcher; uses wc_ConstantCompare; stricter query-param parsing. |
src/internal.h |
Introduces WOLFCERT_HTTP_QUERY_SZ and WOLFCERT_SCEP_MAX_GET_URL with compile-time consistency guards; exposes GET-URL builder for white-box testing. |
src/http.c |
Raises/guards client URL path+query ceiling to support SCEP GET fallback. |
src/est/est_server.c |
Replaces manual constant-time compare with wc_ConstantCompare for Basic-auth check. |
tests/unit/test_scep_msg.c |
Adds unit coverage for fingerprint verification and GET-URL construction/round-trip. |
tests/integration/test_scep_roundtrip.c |
Adds integration coverage for GET fallback + malformed GET rejection via raw socket requests. |
tests/integration/test_scep_poll_roundtrip.c |
Verifies failInfo is surfaced (e.g., badCertId=4) in poll failure. |
tests/interop/scep_micromdm.sh |
Makes micromdm SCEP interop strict pass/fail in both directions; notes DES3 requirement. |
scripts/ci/build-wolfssl.sh |
Enables --enable-des3 in the “full” wolfSSL CI build flags. |
README.md |
Updates canonical wolfSSL configure line to include --enable-des3. |
docs/EMBEDDED.md |
Updates embedded guidance for new heap-vs-stack request buffering and new tunables. |
docs/ARCHITECTURE.md |
Documents GET fallback behavior and trust-bootstrap helper at the architecture level. |
CLAUDE.md |
Mirrors canonical wolfSSL configure line update (--enable-des3). |
Comments suppressed due to low confidence (1)
src/scep/scep_server.c:907
- The operation dispatch uses strncmp() without checking that the value ends at '&' or '\0'. A query like operation=PKIOperationX&message=... would be accepted and routed as PKIOperation, which defeats the intent of strict query matching and can lead to unexpected handler selection.
if (strncmp(op, "GetCACaps", 9) == 0 && strcmp(req.method, "GET") == 0) {
handle_get_ca_caps(s, fd);
}
else if (strncmp(op, "GetNextCACert", 13) == 0 && strcmp(req.method, "GET") == 0) {
handle_get_next_ca_cert(s, fd);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
When the passed caps show the CA does not advertise POSTPKIOperation, carry the pkiMessage base64-encoded and percent-escaped in a GET `message` query parameter instead of POSTing it. run_pki_op chooses POST (the default, and whenever caps are unknown) or GET from the caps; a GET URL longer than WOLFCERT_SCEP_MAX_GET_URL (8 KiB) is refused with WOLFCERT_ERR_UNSUPPORTED so the caller falls back to a POST-capable CA. The in-tree test server learns to accept GET PKIOperation: it decodes the message parameter and dispatches it exactly like a POST body. Its query field gets a dedicated WOLFCERT_HTTP_QUERY_SZ (8 KiB) so the encoded message fits, and the client HTTP path ceiling is raised to match. Tests: test_scep_roundtrip enrolls once over the GET path end to end; test_scep_msg unit-tests URL construction and the oversize rejection. docs/ARCHITECTURE.md and the docs/EMBEDDED.md tunables table updated.
wolfSSL now exposes wc_ConstantCompare as a public API (alongside wc_ForceZero). Replace the three hand-rolled XOR-accumulate constant-time comparisons with it: - the CA fingerprint check (scep_client.c, dropping the local ct_diff), - the challenge-password check (scep_server.c), and - the HTTP Basic-auth credential check (est_server.c). Each keeps its existing length-mismatch guard, and wc_ConstantCompare returns 0 iff equal, so behaviour is unchanged.
micromdm/scep content-encrypts its PKCS#7 pkcsPKIEnvelope with single DES-CBC (1.3.14.3.2.7), which wolfSSL compiles out by default (NO_DES3), so wc_PKCS7_DecodeEnvelopedData returns ALGO_ID_E. Add --enable-des3 to the canonical `full` wolfSSL build (build-wolfssl.sh) so the micromdm interop job can decode both the CertRep (D2) and the PKCSReq (D1) envelopes. This also makes the existing `no-des3` config a real contrast rather than a no-op, since wolfSSL defaults DES off. Mirror the flag into the canonical configure line in README.md and CLAUDE.md. Make scep_micromdm.sh require both directions to pass. This wolfCert PR now depends on the wolfSSL pkcs7_fix PR (EnvelopedData constructed-[0] OCTET STRING decode + DigestInfo NULL-params signature fix), so the earlier self-heal / KNOWN-FAIL tolerance is dropped: D2 and D1 are strict assertions that fail on any interop error and dump the client log. Also fix the D1 check to accept micromdm's PEM cert (it parsed DER before).
SCEP (RFC 8894) gap-filling: GET PKIOperation, CA-fingerprint bootstrap, failInfo, and hardening
Summary
This branch closes several RFC 8894 (SCEP) conformance and robustness gaps in the client and the in-tree test server, and finishes with a small cross-cutting cleanup. It adds the HTTP GET
PKIOperationfallback, an out-of-band CA-fingerprint trust-bootstrap helper, surfaces the CertRep failInfo code, and moves the server's request handling off the stack. No functional behaviour of the existing POST-based flow changes.Five self-contained commits, each green on its own:
scep: surface CertRep failInfo and drop unused renewal new_key paramscep: add wolfcert_scep_verify_ca_fingerprint trust-bootstrap helperscep: add base64 HTTP GET PKIOperation fallback (RFC 8894 4.1)scep,est: use wc_ConstantCompare for constant-time comparisonsci,interop: enable DES3 and make micromdm SCEP interop strictWhat's included
1. CertRep
failInfosurfaced; renewal API cleanupfailInfoattribute from a FAILURECertRepand exposes the code (0–4) instead of discarding it, so a caller can tell why an enrollment was rejected.new_keyparameter fromwolfcert_scep_renewal_req[_ex](it was(void)new_key;— the renewed key already travels inside the CSR).2. CA-fingerprint trust-bootstrap helper
wolfcert_scep_verify_ca_fingerprint()verifies a downloaded CA/RA certificate against a fingerprint obtained out of band — the standard SCEP trust-on-first-use bootstrap.WOLFCERT_SCEP_FP_AUTOselects the algorithm from the fingerprint length (20 / 32 / 64).3. HTTP GET
PKIOperationfallback (RFC 8894 §4.1) + server hardeningPOSTPKIOperation, the client carries the base64/percent-encodedpkiMessagein a GET query, capped byWOLFCERT_SCEP_MAX_GET_URL.PKIOperationand dispatches it exactly like a POST.path/querypoint into it, dropping peak request-handling stack from ~27 KB to under 1 KB (the GET message no longer lands on the stack). Buffer sizes are unchanged; only their storage moved. The EST server and client are untouched.Content-Lengthbody no longer leaks the body buffer when the decodedpkiMessageis installed.operation=/message=are matched as whole parameters (start-of-query or after&) rather than as substrings.500, matching its sibling error branches.4.
wc_ConstantCompareadoptionwc_ConstantCompareas a public API (alongsidewc_ForceZero). Replaces the three hand-rolled XOR-accumulate constant-time comparisons (CA fingerprint, challenge password, HTTP Basic-auth) with it. Net −16 lines; behaviour is identical (returns 0 iff equal).5. CI & interop (micromdm/scep)
--enable-des3added to the canonicalfullwolfSSL CI build. micromdm content-encrypts itspkcsPKIEnvelopewith single DES-CBC (1.3.14.3.2.7), which wolfSSL disables by default (NO_DES3) →ALGO_ID_Ewithout it. This also makes the existingno-des3config a real contrast (previously a no-op). Mirrored into the canonical configure line inREADME.md/CLAUDE.md.pkcs7_fixPR (Fix two PKCS#7/CMS Go-interop gaps: SignedData DigestInfo verify + EnvelopedData definite [0] decrypt wolfssl#10928) — two PKCS#7 fixes:wc_PKCS7_DecodeEnvelopedDataaccepting a definite-length constructed[0]OCTET STRING forencryptedContent, andwc_PKCS7_VerifySignedDatatolerating the SignerInfo digestAlgorithm NULL-params vs DigestInfo mismatch. Do not merge before Fix two PKCS#7/CMS Go-interop gaps: SignedData DigestInfo verify + EnvelopedData definite [0] decrypt wolfssl#10928 reaches wolfSSLmaster— the strict interop job stays red until it does.Public API changes
wolfcert_scep_verify_ca_fingerprint()+WolfCertScepFpAlgenumfailInfocode surfaced on the response statusnew_keyparameter removed fromwolfcert_scep_renewal_req[_ex]Build requirement
This adds one implicit dependency: wolfCert now calls
wc_ConstantCompare, so the linked wolfSSL must provide it (i.e. not built withWOLFSSL_NO_CONST_CMP). This mirrors the assumption already made forwc_ForceZero, so no new configure/check_config.hgate was added.Testing
ctest), both CMake and autoconf.message=, malformed percent-escape, invalid base64) driven over a raw socket; the GET-with-body leak-prevention path; SHA-512 fingerprint (explicit + AUTO).check_ca_fingerprintalso assertsUNSUPPORTEDwhen SHA-1/SHA-512 are absent (explicit + AUTO);test_pki_get_urlround-trips the encoded message (percent-decode → base64-decode → compare) instead of only checking it is non-empty.Notes for reviewers
roundtrip; the allocation uses theheaphint, so static-memory pools are still honoured.docs/EMBEDDED.mdand theinternal.hbuffer comments were updated to reflect the SCEP request buffer moving to the heap.