Skip to content

cli: pin the SCEP GetCACert response - #17

Open
yosuke-wolfssl wants to merge 1 commit into
wolfSSL:mainfrom
yosuke-wolfssl:fix/f_8046
Open

cli: pin the SCEP GetCACert response#17
yosuke-wolfssl wants to merge 1 commit into
wolfSSL:mainfrom
yosuke-wolfssl:fix/f_8046

Conversation

@yosuke-wolfssl

@yosuke-wolfssl yosuke-wolfssl commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Problem

The reference CLI never authenticated the SCEP CA it enrolled against. cmd_enroll fetched GetCACert over the same (usually plaintext) transport it was about to trust, made the first certificate the CSR envelope recipient, and made the whole response the CertRep trust set. Since wolfcert_scep_verify_rep_signer accepts any signer whose SPKI matches any bundle member, an active attacker answering GetCACert with their own RSA certificate both decrypts the CSR with its challengePassword and returns a certificate the client accepts. wolfcert_scep_verify_ca_fingerprint was written for exactly this bootstrap and had zero callers in cli/. A caller using the library API correctly is unaffected.

Fix (cli/wolfcert_client.c)

Adds --ca-fingerprint [sha256:|sha1:|sha512:]HEX, rejected under EST and validated before any network access.

  • A matching certificate becomes the only trust anchor. It is the envelope recipient and the sole CertRep anchor; the bundle-widening fetch is skipped. No match fails closed.
  • getcacerts prints each certificate's SHA-256, so the value can be read once over a trusted path and pinned thereafter.
  • getnextca resolves the current CA through the same pin, closing the circularity its own comment documented.
  • Unpinned SCEP warns, whatever the transport.
  • getnextca wants the CA's own fingerprint, not the RA's — RFC 8894 section 4.6.1 has the current CA sign the roll-over, while a CertRep in an RA deployment is signed by the RA. getcacerts prints both, so one trusted read covers it.
wolfcert-client getcacerts --proto scep --url http://ca.example/scep
# getcacerts: certificate 0 is sha256:3F:A1:...
wolfcert-client enroll --proto scep --url http://ca.example/scep \
    --ca-fingerprint sha256:3F:A1:... --key-type rsa:2048 --subject "CN=dev"

Closes f-8046.

Tests

tests/integration/cli_proto_scoping.sh, the repo's existing CLI test, gains the option scoping and argument rejections plus a group that starts wolfcert-server, reads the fingerprint from getcacerts, and runs enroll and getnextca against a mismatched and a matching pin. The group skips itself when the server was not built, so no new build-file registration is needed.

Not in this PR

Deferred to follow-up issues, all raised in review:

  • A multi-certificate GetCACert fixture, so pin selection is exercised on the split CA/RA shape. Needs new test infrastructure.
  • wolfcert_scep_get_ca_caps's discarded return in scep_enroll — a distinct issue from this one; the pin cannot address it, and the README now documents the exposure.
  • A failed XMALLOC in wolfcert_scep_parse_pki_message surfacing as WOLFCERT_ERR_AUTH rather than a memory error.
  • FIXTURES_REQUIRED on the ctest registration.

Verification

  • 26/26, build clean under the project's warning set.
  • Negative controls: disabling the enforcement branch in scep_enroll or cmd_getnextca makes the matching mismatched-pin case pass.

@yosuke-wolfssl yosuke-wolfssl self-assigned this Aug 19, 2026
Copilot AI lite review requested due to automatic review settings August 19, 2026 01:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the reference CLI’s SCEP flow by adding CA fingerprint pinning so the client can authenticate the GetCACert response out-of-band and avoid trusting attacker-supplied CA/RA material over an untrusted transport.

Changes:

  • Add --ca-fingerprint (SCEP-only) and enforce pin validation before network access; use only the pinned cert as the envelope recipient and CertRep trust anchor.
  • Print SHA-256 fingerprints in getcacerts to support operator pin acquisition; apply the same pin resolution to getnextca.
  • Extend the existing CLI integration test to cover option scoping, argument validation, and end-to-end pinning against wolfcert-server; update docs accordingly.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
cli/wolfcert_client.c Implements --ca-fingerprint parsing/enforcement and pins SCEP enroll/getnextca trust decisions to the matched cert.
tests/integration/cli_proto_scoping.sh Adds argument-validation cases and an end-to-end pinning group using the in-tree test server.
README.md Documents why/when to pin SCEP GetCACert and how to obtain/use the fingerprint via the CLI.
docs/MIGRATING-FROM-WOLFSCEP.md Updates migration guidance to mention the new CLI fingerprint pinning option.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tests/integration/cli_proto_scoping.sh Outdated

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #17

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.

Comment thread cli/wolfcert_client.c Outdated
Comment thread cli/wolfcert_client.c
Comment thread cli/wolfcert_client.c Outdated
Comment thread cli/wolfcert_client.c

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #17

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.

Comment thread cli/wolfcert_client.c
Comment thread cli/wolfcert_client.c
Comment thread cli/wolfcert_client.c
Comment thread cli/wolfcert_client.c

@Frauschi Frauschi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read through the pinning change. The trust bootstrap itself looks right - find_pinned_cert selects, frees the rejected certificates and fails closed, and making the pinned certificate the sole envelope recipient and CertRep anchor is the correct call. What is below is one design question plus coverage and documentation gaps; nothing blocking.

The one worth a decision is getnextca. Pinning the RA for enroll, as the README now tells you to, makes getnextca fail with a message that reads like a broken pin, because the roll-over is signed by the CA. I think the narrowing is right and the answer is to say so rather than widen the anchor back - details inline.

The recurring theme in the rest is that wolfcert-server serves a single CA certificate, so the multi-certificate half of the pin selection never runs, and the case that claims to guard the stale-error path never reaches it.

ctest registration comment for cli_proto_scoping is now false — the test starts a network server (tests/CMakeLists.txt:117-118)
The comment above add_test(NAME cli_proto_scoping ...) still reads "Every case fails before any network access, so no server is involved". The new pinning group binds 18088/18188/18288/18388 and forks wolfcert-server, so that is no longer true - worth fixing, since that comment is what the next person uses to decide whether this test is safe to run in a sandbox or in parallel.

The registration also declares no dependency or fixture on the wolfcert-server target and passes only $<TARGET_FILE:wolfcert-client>, relying on the script deriving its sibling. That works under both CMake and the automake AM_TESTS_ENVIRONMENT, so nothing is broken, but a build dependency (or FIXTURES_REQUIRED) would stop the group silently skipping itself when ctest is driven without a full build.

Comment thread cli/wolfcert_client.c
Comment thread tests/integration/cli_proto_scoping.sh Outdated
Comment thread tests/integration/cli_proto_scoping.sh
Comment thread tests/integration/cli_proto_scoping.sh Outdated
Comment thread tests/integration/cli_proto_scoping.sh
Comment thread README.md
Comment thread cli/wolfcert_client.c

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #17

Scan targets checked: wolfcert-bugs, wolfcert-src

Fenrir result: Approved ✅

No new issues found in the changed files.

Advisory only — this automated result does not count as a GitHub approval.

@yosuke-wolfssl

Copy link
Copy Markdown
Contributor Author

Hello @Frauschi ,
I reworked on it. Can you review this again ?
Please note that some issues are not addressed here because they are pre-existing gaps.
Especially we need new infrastructure for tests of the split CA/RA shape, so I'll take it as follow-up.
They are described in PR body honestly.

@Frauschi Frauschi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm completely fine with the fixes and the deferrals to later follow-ups.

Please fix conflicts after #15 has landed.

- --ca-fingerprint takes [sha256:|sha1:|sha512:]HEX, and is refused
  under EST. parse_ca_fingerprint decodes it into a CaPin;
  scep_pin_setup resolves it, and notes that an unpinned run trusts
  the served CA.
- pem_cert_at, find_pinned_cert, der_to_pem and print_ca_fingerprints
  select, re-encode and report the certificates of a GetCACert
  response, numbered over the ones that decode.
- With a pin, scep_enroll makes the match the envelope recipient and
  the only CertRep anchor and skips the bundle fetch; cmd_getcacerts
  writes only the match; cmd_getnextca resolves the current CA through
  the pin, and on WOLFCERT_ERR_AUTH names the CA as the signer of the
  roll-over.
- cli_proto_scoping.sh adds the option scoping, the argument
  rejections, and a wolfcert-server group covering a mismatched and a
  matching pin, with a shell repeat helper and a whole-second sleep
  fallback; tests/CMakeLists.txt and Makefile.am describe the
  registration.
- README.md and docs/MIGRATING-FROM-WOLFSCEP.md document the flag,
  which certificate getnextca wants pinned, and that the pin leaves
  GetCACaps unauthenticated.

Issue: F-8046
@yosuke-wolfssl

Copy link
Copy Markdown
Contributor Author

Hi @Frauschi ,
Thanks, the conflict was solved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants