Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,12 @@ Minimal orientation:
(`<wolfcert/*.h>` and `"internal.h"`) -> wolfSSL (`<wolfssl/...>`) ->
system headers.
- **Session vs one-shot APIs.** The non-blocking session variants
(`wolfcert_http_session_request_nb`, `wolfcert_est_session_*_nb`) are
the only non-blocking entry points; one-shot
`wolfcert_http_request` / `wolfcert_est_*` / `wolfcert_scep_*`
(`wolfcert_http_session_request_nb`, `wolfcert_est_session_*_nb`,
`wolfcert_scep_session_*_nb`) are the only non-blocking entry points;
one-shot `wolfcert_http_request` / `wolfcert_est_*` / `wolfcert_scep_*`
calls are blocking by design. DNS + initial TCP connect remain
synchronous even in non-blocking mode.
synchronous even in non-blocking mode. The SCEP session (unlike EST)
does not require TLS, since SCEP authenticates at the pkiMessage layer.
- **SCEP is RSA-only** (per RFC 8894). The SCEP entry points reject
non-RSA keys with `WOLFCERT_ERR_UNSUPPORTED`. EST is the right
protocol for Ed25519 / Ed448 / ML-DSA.
Expand Down
4 changes: 3 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,14 @@ test_scep_msg_SOURCES = tests/unit/test_scep_msg.c
test_scep_msg_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src
test_scep_msg_LDADD = libwolfcert.la $(WOLFSSL_LIBS)
if WOLFCERT_HAVE_SERVER
check_PROGRAMS += test_scep_roundtrip test_scep_poll_roundtrip
check_PROGRAMS += test_scep_roundtrip test_scep_poll_roundtrip test_scep_async_roundtrip
test_scep_roundtrip_SOURCES = tests/integration/test_scep_roundtrip.c
test_scep_roundtrip_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src
test_scep_roundtrip_LDADD = libwolfcert.la $(WOLFSSL_LIBS) -lpthread
test_scep_poll_roundtrip_SOURCES = tests/integration/test_scep_poll_roundtrip.c
test_scep_poll_roundtrip_LDADD = libwolfcert.la $(WOLFSSL_LIBS) -lpthread
test_scep_async_roundtrip_SOURCES = tests/integration/test_scep_async_roundtrip.c
test_scep_async_roundtrip_LDADD = libwolfcert.la $(WOLFSSL_LIBS) -lpthread
endif
endif

Expand Down
16 changes: 15 additions & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,17 @@ the fingerprint length) and constant-time-compares it, returning
`WOLFCERT_ERR_AUTH` on mismatch. Verify the bundle this way before using it as
the `ca_bundle` trust set for enrollment.

**Keep-alive / async sessions.** Alongside the one-shot calls, PKCSReq,
RenewalReq and GetCertInitial have session variants
(`wolfcert_scep_session_pkcs_req_ex`/`_nb`, etc.) that reuse one connection,
built on the same non-blocking HTTP session as EST (see the session section
below). Fetch caps + the CA cert with the one-shot getters first, then open a
session with `wolfcert_scep_session_open` (blocking) or
`wolfcert_scep_session_open_async` (event-loop). Each round trip is
prepared (envelope + sign), sent, and its CertRep parsed as one logical step;
in async mode only the HTTP transport is pumped through `WANT_READ`/`WANT_WRITE`
while the crypto stays synchronous.

**Signed attributes.** The CertRep carries the full RFC 8894 §3.1
signed-attribute set (including `recipientNonce`) — up to 9 entries alongside
the CMS auto-defaults. wolfSSL's PKCS#7 encoder grows its signed-attribute
Expand Down Expand Up @@ -297,7 +308,10 @@ for (;;) {
Key points:

- **Only the session API is non-blocking.** One-shot `wolfcert_http_request` /
`wolfcert_est_*` / `wolfcert_scep_*` calls are blocking by design.
`wolfcert_est_*` / `wolfcert_scep_*` calls are blocking by design. Both EST
(`wolfcert_est_session_*_nb`) and SCEP (`wolfcert_scep_session_*_nb`) offer
non-blocking session variants; the SCEP session additionally does not require
TLS, since SCEP authenticates at the pkiMessage layer.
- **DNS and the initial TCP connect stay synchronous.** Only socket operations
*after* the session is open are non-blocking; if you can't afford a blocking
connect, resolve and connect the socket yourself.
Expand Down
16 changes: 4 additions & 12 deletions src/est/est_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -422,19 +422,11 @@ static int est_session_open_common(const WolfCertServerCfg* srv, int nonblocking
"the server certificate (RFC 7030)");
}

size_t origin_len = strlen(u.scheme) + 3 + strlen(u.host) + 16;
char* origin = (char*)WOLFCERT_XMALLOC(origin_len, heap);
if (origin == NULL) {
wolfcert_http_url_free(&u);
return WOLFCERT_ERR_MEMORY;
}

if ((u.tls && u.port == 443) || (!u.tls && u.port == 80))
snprintf(origin, origin_len, "%s://%s", u.scheme, u.host);
else
snprintf(origin, origin_len, "%s://%s:%d", u.scheme, u.host, u.port);

char* origin = NULL;
rc = wolfcert_http_url_origin(&u, heap, &origin);
wolfcert_http_url_free(&u);
if (rc != WOLFCERT_OK)
return rc;

WolfCertEstSession* s = (WolfCertEstSession*)WOLFCERT_XMALLOC(sizeof(*s), heap);
if (s == NULL) {
Expand Down
27 changes: 27 additions & 0 deletions src/http.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,33 @@ WOLFCERT_TEST_VIS void wolfcert_http_url_free(WolfCertUrl* u)
u->scheme = u->host = u->path = NULL;
}

/* Build the "scheme://host[:port]" origin for a parsed URL into a freshly
* allocated buffer (owned by the caller, free with WOLFCERT_XFREE). The default
* port (443 for TLS, 80 otherwise) is omitted. Shared by the EST and SCEP
* session opens so the two origin builders cannot drift. */
WOLFCERT_TEST_VIS int wolfcert_http_url_origin(const WolfCertUrl* u, void* heap,
char** out_origin)
{
size_t origin_len;
char* origin;

if (u == NULL || u->scheme == NULL || u->host == NULL || out_origin == NULL)
return WOLFCERT_ERR_BAD_ARG;

origin_len = strlen(u->scheme) + 3 + strlen(u->host) + 16;
origin = (char*)WOLFCERT_XMALLOC(origin_len, heap);
if (origin == NULL)
return WOLFCERT_ERR_MEMORY;

if ((u->tls && u->port == 443) || (!u->tls && u->port == 80))
snprintf(origin, origin_len, "%s://%s", u->scheme, u->host);
else
snprintf(origin, origin_len, "%s://%s:%d", u->scheme, u->host, u->port);

*out_origin = origin;
return WOLFCERT_OK;
}

static char* dup_range(const char* s, const char* e, void* heap)
{
size_t n = (size_t)(e - s);
Expand Down
2 changes: 2 additions & 0 deletions src/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ typedef struct {

WOLFCERT_TEST_VIS int wolfcert_http_url_parse(const char* url, WolfCertUrl* out, void* heap);
WOLFCERT_TEST_VIS void wolfcert_http_url_free (WolfCertUrl* u);
WOLFCERT_TEST_VIS int wolfcert_http_url_origin(const WolfCertUrl* u, void* heap,
char** out_origin);

/* Base64 helpers. `_encode` is single-line (no newlines) - the right
* default for HTTP header values and for wolfCert's own server-side
Expand Down
Loading
Loading