Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
643f33f
Reject an EST listener configured without a TLS identity
Frauschi Sep 9, 2026
204c701
http: keep IPv6 literals bracketed in origins and Host headers
Frauschi Sep 9, 2026
e75492b
Carry every CSR subject RDN into the issued certificate
Frauschi Sep 9, 2026
766eb8a
Fail server startup when the CA store cannot be read or written
Frauschi Sep 9, 2026
65e2289
Interrupt an idle accepted connection on server stop
Frauschi Sep 9, 2026
308cc04
Verify the stored CA certificate matches the stored CA key
Frauschi Sep 9, 2026
41a1a9b
Bound the non-blocking read to the remaining response allowance
Frauschi Sep 9, 2026
b8f6c5b
Stop the URL authority scan at a query or fragment
Frauschi Sep 9, 2026
cc97254
Assert keyUsage on the generated self-signed CA certificate
Frauschi Sep 9, 2026
be7e9ce
Report CA-store read and rollback failures as themselves
Frauschi Sep 11, 2026
7f6c39d
Bound server I/O by shutdown, not by which direction stalled
Frauschi Sep 11, 2026
85ed9be
Pin the givenName drop between CSR and issued certificate
Frauschi Sep 11, 2026
6de13dd
Gate the server tests on the server, not on a protocol
Frauschi Sep 11, 2026
eaee403
Carry street and the string encoding into the issued certificate
Frauschi Sep 11, 2026
5bfce12
Mark basicConstraints critical on the generated CA
Frauschi Sep 11, 2026
3ceaea7
Correct three RFC citations and drop the restated contracts
Frauschi Sep 11, 2026
475452f
Share the helpers that had grown second copies
Frauschi Sep 11, 2026
54cd99f
Address the non-blocking review points
Frauschi Sep 11, 2026
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
4 changes: 3 additions & 1 deletion .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ jobs:
- { name: cmake-no-builtin-transport, os: ubuntu-latest, build: cmake, wolfssl: full, cmake_extra: "-DWOLFCERT_ENABLE_BUILTIN_TRANSPORT=OFF -DWOLFCERT_ENABLE_POSIX_STORE=OFF -DWOLFCERT_ENABLE_CLI=OFF -DWOLFCERT_ENABLE_SERVER=OFF" }
# ---- Constrained builds (unit tests only: single-thread / no-malloc) ----
- { name: cmake-static-mem, os: ubuntu-latest, build: cmake, wolfssl: static-mem, cmake_extra: -DWOLFCERT_ENABLE_SERVER=OFF, ctest_exclude: "http|tls|roundtrip" }
- { name: cmake-no-malloc, os: ubuntu-latest, build: cmake, wolfssl: no-malloc, ctest_exclude: "http|tls|roundtrip|est" }
# stop_idle is named explicitly: its TLS cases carry no "tls" in the
# test name, and TLS over a static pool is out of scope here.
- { name: cmake-no-malloc, os: ubuntu-latest, build: cmake, wolfssl: no-malloc, ctest_exclude: "http|tls|roundtrip|est|stop_idle" }
# ---- macOS reduced subset ----
- { name: cmake-full-macos, os: macos-latest, build: cmake, wolfssl: full }
- { name: cmake-nonrsa-macos, os: macos-latest, build: cmake, wolfssl: est-only-nonrsa, cmake_extra: -DWOLFCERT_ENABLE_SCEP=OFF }
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,9 @@ jobs:
wolfssl: no-malloc
# Non-TLS crypto/cert/PKCS7 units only: TLS + threads over a static
# pool is a separate wolfSSL concern, out of scope for the
# allocation-free validation.
ctest_exclude: "http|tls|roundtrip|est"
# allocation-free validation. stop_idle is named because its TLS
# cases carry no "tls" in the test name.
ctest_exclude: "http|tls|roundtrip|est|stop_idle"
# ---- macOS ----
- name: cmake-full-macos
os: macos-latest
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/sanitizers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,10 @@ jobs:
cmake --build build -j "$(nproc)"
# The roundtrip tests spin the server on a background pthread while the
# client drives it on the main thread -> the real concurrency surface.
# server_stop_idle belongs here too: it stops the server from a second
# thread, which is the one shutdown path nothing else exercises.
ctest --test-dir build -j "$(nproc)" --output-on-failure \
-R 'roundtrip|tls_http'
-R 'roundtrip|tls_http|stop_idle'

valgrind:
name: valgrind (representative subset)
Expand Down
12 changes: 9 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ configure time if the installed wolfSSL lacks any of `HAVE_PKCS7`,
`WOLFSSL_KEY_GEN`, `WOLF_CRYPTO_CB`, `WOLFSSL_BASE64_ENCODE`,
`OPENSSL_EXTRA`, `WOLFSSL_ALT_NAMES`, or `WOLFSSL_CERT_NAME_ALL`, or if
it was built with `NO_AES` / `NO_SHA256`, or if it provides neither
TLS 1.2 nor TLS 1.3.
TLS 1.2 nor TLS 1.3. With ML-DSA enabled it additionally needs
`WOLFSSL_MLDSA_CHECK_KEY` (`wc_MlDsaKey_CheckKey()`), which reloading an
ML-DSA CA from a store calls -- checked when `src/key_algs.c` compiles,
since only `dilithium.h` resolves that macro. `--enable-mldsa` gives it by
default; it is lost only if wolfSSL is built with
`WOLFSSL_DILITHIUM_NO_CHECK_KEY` or `WOLFSSL_MLDSA_VERIFY_ONLY`.

**Key algorithms are gated** by `WOLFCERT_HAVE_<ALG>` (RSA, ECC,
ED25519, ED448, MLDSA). RSA, ECC, Ed25519, Ed448 and ML-DSA are each
Expand Down Expand Up @@ -96,9 +101,10 @@ Unit tests live in `tests/unit/`; end-to-end flows in
After a build with `-DWOLFCERT_ENABLE_CLI=ON` (the default):

```sh
build/wolfcert-server --proto est --listen 127.0.0.1:8443
build/wolfcert-server --proto est --listen 127.0.0.1:8443 \
--tls-cert server.crt --tls-key server.key
build/wolfcert-client enroll --proto est \
--url http://127.0.0.1:8443/.well-known/est \
--url https://127.0.0.1:8443/.well-known/est --trust server.crt \
--key-type ecc:256 --subject "CN=dev" \
--out-key dev.key --out-cert dev.crt
```
Expand Down
12 changes: 12 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ test_smoke_LDADD = libwolfcert.la $(WOLFSSL_LIBS)
test_keygen_SOURCES = tests/unit/test_keygen.c
test_keygen_LDADD = libwolfcert.la $(WOLFSSL_LIBS)
test_csr_SOURCES = tests/unit/test_csr.c
test_csr_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src
test_csr_LDADD = libwolfcert.la $(WOLFSSL_LIBS)
test_store_SOURCES = tests/unit/test_store.c
test_store_LDADD = libwolfcert.la $(WOLFSSL_LIBS)
Expand Down Expand Up @@ -199,6 +200,17 @@ test_scep_async_roundtrip_LDADD = libwolfcert.la $(WOLFSSL_LIBS) -lpthread
endif
endif

# Protocol-agnostic server coverage: gated on the server alone, so a build with
# either protocol disabled keeps it.
if WOLFCERT_HAVE_SERVER
check_PROGRAMS += test_server_stop_idle test_server_ca_store
test_server_stop_idle_SOURCES = tests/integration/test_server_stop_idle.c
test_server_stop_idle_LDADD = libwolfcert.la $(WOLFSSL_LIBS) -lpthread
test_server_ca_store_SOURCES = tests/unit/test_server_ca_store.c
test_server_ca_store_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src
test_server_ca_store_LDADD = libwolfcert.la $(WOLFSSL_LIBS) -lpthread
endif

TESTS = $(check_PROGRAMS)

# The CLI's protocol scoping and keyword validation, driven against the built
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ on the include path. See [`docs/EMBEDDED.md`](docs/EMBEDDED.md#configuring-wolfc
Start the bundled test server (issues from an auto-generated local CA):

```sh
./wolfcert-server --proto est --listen 127.0.0.1:8443
# EST is TLS-only (RFC 7030), so it needs a server identity for the
# listen address; SCEP authenticates at the pkiMessage layer instead.
./wolfcert-server --proto est --listen 127.0.0.1:8443 \
--tls-cert server.crt --tls-key server.key
./wolfcert-server --proto scep --listen 127.0.0.1:8088
```

Expand All @@ -94,7 +97,7 @@ Enroll a certificate from the CLI:
```sh
# EST
./wolfcert-client enroll --proto est \
--url http://127.0.0.1:8443/.well-known/est \
--url https://127.0.0.1:8443/.well-known/est --trust server.crt \
--key-type ecc:256 --subject "CN=device-1,O=Acme" \
--san-dns device-1.local --out-key dev.key --out-cert dev.crt

Expand Down
12 changes: 10 additions & 2 deletions cli/wolfcert_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@

/*
* wolfcert-server - minimal EST/SCEP test server. Issues certificates
* against a local CA generated on startup. Plaintext HTTP only.
* against a local CA generated on startup. EST needs --tls-cert/--tls-key
* (RFC 7030); SCEP may be served over plaintext HTTP.
*/

#define _POSIX_C_SOURCE 200809L
Expand Down Expand Up @@ -67,7 +68,8 @@ static void print_usage(FILE* out)
" --listen HOST:PORT Bind address (default 0.0.0.0:8080)\n"
" --basic USER:PASS Require HTTP Basic auth (EST enroll)\n"
" --challenge PASS Require this SCEP challengePassword in the CSR\n"
" --tls-cert PEMFILE Terminate TLS with this server certificate (PEM)\n"
" --tls-cert PEMFILE Terminate TLS with this server certificate (PEM);\n"
" required for --proto est (RFC 7030)\n"
" --tls-key PEMFILE Private key for --tls-cert (PEM)\n"
" --tls-client-ca PEMFILE Require mutual TLS; verify clients against this CA\n"
" --scep-require-approval Defer SCEP PKCSReq/RenewalReq (pkiStatus=PENDING); issue\n"
Expand Down Expand Up @@ -293,6 +295,12 @@ int main(int argc, char** argv)
return 1;
}

if (sel == WOLFCERT_PROTO_EST && tls_cert == NULL) {
fprintf(stderr, "wolfcert-server: --proto est requires --tls-cert and "
"--tls-key (RFC 7030 has no plaintext mode)\n");
return 1;
}

if (host == NULL)
host = strdup("0.0.0.0");

Expand Down
6 changes: 5 additions & 1 deletion docs/EMBEDDED.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,16 @@ the request completes) so it never counts against the stack budget.
| `WOLFCERT_HTTP_QUERY_SZ` | `8192` | sized to hold a base64 GET `PKIOperation` message; on the SCEP server it extends the heap read buffer (`REQ_BUF_SZ + QUERY_SZ`) that `query` points into |
| `WOLFCERT_HTTP_AUTH_BUF_SZ` | `512` | client Basic-auth header line (`http.c`) |
| `WOLFCERT_HTTP_MAX_PATH_LEN` | `8192` | client-side ceiling on a request URL's path+query (`http.c`) |
| `WOLFCERT_HTTP_HEADER_BUDGET` | `8192` | client response allowance added to the caller's body cap, bounding the status line plus header block. Both the blocking and the non-blocking reader grow their accumulator to `max_response_bytes + this` (`http.c`) |
| `WOLFCERT_SCEP_MAX_GET_URL` | `8192` | client cap on a GET `PKIOperation` URL; a larger message is refused with `WOLFCERT_ERR_UNSUPPORTED` so the caller POSTs (`internal.h`) |

Shrinking `WOLFCERT_HTTP_REQ_BUF_SZ` lowers the largest request header
block the server accepts; `WOLFCERT_HTTP_PATH_SZ` / `WOLFCERT_HTTP_QUERY_SZ`
lower the longest request path / query; `WOLFCERT_HTTP_AUTH_BUF_SZ` lowers the
longest Basic-auth credential the client can send. A POST-only SCEP deployment
longest Basic-auth credential the client can send. `WOLFCERT_HTTP_HEADER_BUDGET`
trims the client's response accumulator, and with it the largest response
header block it will accept, so keep it above the headers your CA actually
sends. A POST-only SCEP deployment
can trim `WOLFCERT_HTTP_QUERY_SZ` (and, on the client, `WOLFCERT_SCEP_MAX_GET_URL`
and `WOLFCERT_HTTP_MAX_PATH_LEN`) back down. Example:

Expand Down
Loading
Loading