Skip to content

SCEP: add keep-alive / async SCEP session API - #12

Merged
philljj merged 1 commit into
wolfSSL:mainfrom
Frauschi:scep_async
Jul 28, 2026
Merged

SCEP: add keep-alive / async SCEP session API#12
philljj merged 1 commit into
wolfSSL:mainfrom
Frauschi:scep_async

Conversation

@Frauschi

Copy link
Copy Markdown
Contributor

Summary

Adds a keep-alive / non-blocking SCEP (RFC 8894) client session, the SCEP counterpart to the existing EST session. This is the last piece needed for feature-equivalence with wolfSCEP: SCEP enrollment can now run several PKIOperation round trips over one connection, and drive them non-blocking through a caller-owned event loop (poll/epoll/kqueue).

No public API is changed or removed; this is purely additive plus an internal refactor shared with the EST path.

What's included

  • WolfCertScepSession with blocking and non-blocking opens, plus PKCSReq, RenewalReq and GetCertInitial in _ex (blocking) and _nb (WANT_READ/WANT_WRITE pumped) forms.
  • Internal refactor: do_scep_round_trip split into reusable scep_prepare (envelope + sign), scep_build_transport (POST vs base64 GET), and scep_finish (parse + verify + de-envelop). The one-shot and session paths share these; one-shot behavior is unchanged.
  • Shared origin builder: the scheme://host[:port] construction duplicated by the EST and SCEP session opens is factored into wolfcert_http_url_origin so the two cannot drift.
  • Docs and build wiring: ARCHITECTURE.md, EMBEDDED.md context, CLAUDE.md, plus CMake + autoconf registration of the new test.

New public API (wolfcert/scep.h)

Function Purpose
wolfcert_scep_session_open / _open_async Open a blocking / non-blocking session
wolfcert_scep_session_close Tear down; zeroizes any in-flight private-key copy
wolfcert_scep_session_fd Backing socket fd for poll/epoll/kqueue
wolfcert_scep_session_pkcs_req_ex / _nb Enroll (PKCSReq)
wolfcert_scep_session_renewal_req_ex / _nb Renew (RenewalReq)
wolfcert_scep_session_get_cert_initial_ex / _nb Poll a PENDING enrollment (GetCertInitial)

Design notes

  • Mirrors the EST session in structure (one round trip in flight at a time, _ex/_nb split, same non-blocking HTTP session layer). The crypto stays synchronous; only the HTTP POST/GET is pumped through the state machine. The transactionID, senderNonce, and finish-phase inputs (CA bundle, signer cert/key) are captured in the session so they survive across pumps.
  • No TLS requirement, unlike EST. SCEP authenticates at the pkiMessage layer (the CMS signature is bound to the CA/RA bundle and the recipientNonce echo is enforced), so a plaintext http:// endpoint is accepted. An https:// endpoint, however, now requires verify_server rather than silently running an unauthenticated handshake.

Hardening and correctness

  • RNG return codes checked in scep_prepare: a wc_InitRng_ex / wc_RNG_GenerateBlock failure now fails the round trip instead of building a pkiMessage over an uninitialized transactionID and anti-replay senderNonce. (Pre-existing behavior in the one-shot path; fixed for both, now that the code is shared.)
  • Session misuse guards, all returning WOLFCERT_ERR_BAD_ARG:
    • a mismatched _ex/_nb pairing against the session's open mode (this also makes an in-flight-buffer / private-key-copy leak unreachable),
    • starting a different operation while one is in flight,
    • resuming a poll with a different WolfCertScepResult* than the one captured at begin (guards against a silently-empty result or a dangling-pointer write).

Testing

tests/integration/test_scep_async_roundtrip.c drives the in-tree test server end to end:

Scenario Covers
A Async enrollment; blocking-session enrollment + blocking RenewalReq
B Async PENDING -> GetCertInitial poll (keep-alive)
C Async enrollment -> async RenewalReq (keep-alive)
D Misuse guards: _ex-on-async, _nb-on-blocking, in-flight op-type mismatch
E Blocking PENDING -> GetCertInitial poll (get_cert_initial_ex)
F Session forced onto the base64 GET transport

The async scenarios are pumped through a real poll(2) loop. All 25 project tests pass (including the 12 EST tests, which exercise the shared origin-builder refactor); build is warning-clean.

@Frauschi Frauschi self-assigned this Jul 21, 2026

@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 #12

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 tests/integration/test_scep_async_roundtrip.c Outdated
Comment thread tests/integration/test_scep_async_roundtrip.c Outdated
Comment thread tests/integration/test_scep_async_roundtrip.c Outdated
Comment thread tests/integration/test_scep_async_roundtrip.c 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 #12

Scan targets checked: wolfcert-bugs
Failed targets: 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.

Comment thread src/scep/scep_client.c
Comment thread tests/integration/test_scep_async_roundtrip.c Outdated
Comment thread tests/integration/test_scep_async_roundtrip.c Outdated
Comment thread src/scep/scep_client.c
Comment thread tests/integration/test_scep_async_roundtrip.c Outdated
Comment thread tests/integration/test_scep_async_roundtrip.c 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 #12

Scan targets checked: wolfcert-bugs, wolfcert-src

Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Findings are non-blocking.

Comment thread tests/integration/test_scep_async_roundtrip.c
Comment thread tests/integration/test_scep_async_roundtrip.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 #12

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.

Comment thread tests/integration/test_scep_async_roundtrip.c
Comment thread src/scep/scep_client.c
Comment thread tests/integration/test_scep_async_roundtrip.c Outdated
Comment thread tests/integration/test_scep_async_roundtrip.c
Comment thread src/scep/scep_client.c
Comment thread tests/integration/test_scep_async_roundtrip.c 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 #12

Scan targets checked: wolfcert-bugs, wolfcert-src

No new issues found in the changed files. ✅

@philljj philljj 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.

First pass, looks good, just a few suggestions.

Comment thread src/scep/scep_client.c
Comment thread src/scep/scep_client.c Outdated
Comment thread src/http.c
Comment thread tests/integration/test_scep_async_roundtrip.c

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 adds a keep-alive, non-blocking SCEP client session API (mirroring the existing EST session), and refactors shared SCEP round-trip logic so one-shot and session paths reuse common prepare/transport/finish helpers. It also factors the shared scheme://host[:port] origin builder into wolfcert_http_url_origin to keep EST/SCEP session-open behavior consistent.

Changes:

  • Add public WolfCertScepSession API with blocking (*_ex) and non-blocking (*_nb) round trips (PKCSReq, RenewalReq, GetCertInitial).
  • Refactor SCEP client internals into scep_prepare, scep_build_transport, and scep_finish, shared by one-shot and session implementations.
  • Add new unit/integration test coverage and build wiring for the origin helper and async SCEP session roundtrips.

Reviewed changes

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

Show a summary per file
File Description
wolfcert/scep.h Introduces new public SCEP session API and documents async usage/contracts.
src/scep/scep_client.c Implements the session API and refactors SCEP round-trip logic into reusable helpers.
src/http.c Adds wolfcert_http_url_origin helper for consistent origin construction.
src/internal.h Exposes wolfcert_http_url_origin to internal callers/tests.
src/est/est_client.c Switches EST session open to use the shared origin helper.
tests/unit/test_http.c Adds unit tests for wolfcert_http_url_origin.
tests/integration/test_scep_async_roundtrip.c Adds end-to-end poll-driven async SCEP session integration test scenarios.
tests/CMakeLists.txt Registers the new async SCEP integration test under CMake.
Makefile.am Registers the new async SCEP integration test under autoconf/automake.
docs/ARCHITECTURE.md Documents SCEP keep-alive/async sessions and non-blocking behavior constraints.
CLAUDE.md Updates repository guidance to include the new SCEP non-blocking session entry points.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/scep/scep_client.c
@philljj philljj assigned Frauschi and unassigned wolfSSL-Bot Jul 27, 2026
Add WolfCertScepSession with blocking (wolfcert_scep_session_open) and
non-blocking (wolfcert_scep_session_open_async) opens, plus session
PKCSReq, RenewalReq and GetCertInitial in _ex (blocking) and _nb
(WANT_READ/WANT_WRITE pumped) forms, with wolfcert_scep_session_fd and
wolfcert_scep_session_close. The session reuses the same non-blocking
HTTP session layer as EST. Unlike EST it does not require TLS, since
SCEP authenticates at the pkiMessage layer, so a plaintext http://
endpoint is accepted.

do_scep_round_trip is refactored into reusable scep_prepare (envelope +
sign, producing the transactionID and senderNonce) and scep_finish
(parse + verify + de-envelop) phases, with the POST/GET transport
decision factored into scep_build_transport. The one-shot and session
paths share these; existing entry-point behavior is unchanged.

An async round trip builds the message once, pumps only the HTTP POST/GET
through the session state machine, then parses the CertRep on completion;
the crypto stays synchronous. The txid, senderNonce and finish-phase
inputs (CA bundle, signer cert/key) are captured in the session so they
survive across pumps.

scep_prepare now checks the wc_InitRng_ex / wc_RNG_GenerateBlock return
codes and fails the round trip on error, rather than building a
pkiMessage over an uninitialized transactionID and anti-replay
senderNonce. The session records which PKIOperation occupies its single
in-flight slot and rejects an async _nb resume that names a different
operation or passes a different WolfCertScepResult* out than the one
captured at begin; it also records whether it was opened blocking or
async so a mismatched _ex/_nb call is rejected with WOLFCERT_ERR_BAD_ARG
instead of overwriting (and leaking) the in-flight buffers - including
the zeroized private-key copy - or blocking on a non-blocking socket.
Like the one-shot path a plaintext http:// session is accepted, but an
https:// session now requires verify_server rather than silently running
an unauthenticated TLS handshake. The scheme://host[:port] origin builder
shared by the EST and SCEP session opens is factored into
wolfcert_http_url_origin so the two cannot drift.

scep_prepare hands the effective transactionID back on the heap rather
than filling a fixed buffer, so a caller-supplied value - whatever the
server chose for the request being polled - is carried whole by the
one-shot and the session path alike. build_signed_attribs used to copy
the attribute through a 128-byte NUL-terminated buffer and silently
truncate anything longer, which put an identifier on the wire naming a
different transaction than the one being polled for; it now encodes by
length, and a value too large for the attribute scratch buffer is an
error rather than a quiet trim.

Neither the transactionID nor the senderNonce is a secret, since both are
sent in the clear as signed attributes, but the RNG scratch behind them
is cleared once it is dead and on the RNG failure path, and the session
clears its senderNonce when a round trip ends.

Tests: test_scep_async_roundtrip drives a direct async enrollment; a
blocking-session enrollment followed by a blocking RenewalReq; an async
PENDING -> GetCertInitial poll and its blocking mirror
(GetCertInitial_ex); an async enrollment followed by an async RenewalReq;
and a session forced onto the base64 GET transport. It also exercises the
session misuse guards: an _ex call on an async session, an _nb call on a
blocking session, and starting a second operation while one is in flight.
The async scenarios are pumped through a real poll(2) loop, which treats
POLLERR and POLLNVAL as fatal since poll(2) can report readiness and an
error event in the same call. test_scep_msg checks that a 200-byte
transactionID survives a build/parse round trip byte for byte, and the
one-shot and session polls each carry a transactionID longer than the
generated one through to the server. Registered in both CMake and
autoconf. ARCHITECTURE.md, EMBEDDED context and CLAUDE.md notes updated.
@Frauschi Frauschi removed their assignment Jul 28, 2026
@Frauschi
Frauschi requested a review from philljj July 28, 2026 11:04
@philljj
philljj merged commit 339cda6 into wolfSSL:main Jul 28, 2026
21 checks passed
@Frauschi
Frauschi deleted the scep_async branch July 28, 2026 14:43
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