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
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -978,7 +978,9 @@ UNIT_TEST_SRCS:=src/test/unit/unit.c \
src/test/unit/unit_tests_arp_regression.c \
src/test/unit/unit_tests_dns_edges.c \
src/test/unit/unit_tests_misc_edges.c \
src/test/unit/unit_tests_vlan.c
src/test/unit/unit_tests_vlan.c \
src/test/unit/unit_tests_wolfcert.c \
src/port/wolfcert_io.c

unit: build/test/unit

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ This port follows the same model as the POSIX wrapper:
Module how-tos:

- [TLS over wolfIP](docs/tls_howto.md): running wolfSSL/TLS on wolfIP sockets, the I/O-callback bridge, and non-blocking handshakes
- [Certificate enrolment (wolfCert)](docs/wolfcert_howto.md): running wolfCert's EST/SCEP client on wolfIP sockets, the transport vtable, and name resolution
- [HTTP/HTTPS server](docs/http_server_howto.md): the `src/http/` server module, handler registration, and enabling HTTPS
- [IPsec ESP](docs/ipsec_esp_howto.md): securing traffic with ESP transport mode, SA setup, and Linux `ip xfrm` interop
- [wolfGuard (FIPS WireGuard)](docs/wolfguard_howto.md): the in-stack WireGuard tunnel, peer/key setup, and kernel interop
Expand Down
1 change: 1 addition & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ The core socket and stack APIs are documented below. Optional modules and
features have dedicated getting-started guides:

- [TLS over wolfIP](tls_howto.md) — running wolfSSL/TLS on wolfIP sockets (`WOLFSSL_WOLFIP`), the I/O-callback bridge, and non-blocking handshakes.
- [Certificate enrolment (wolfCert)](wolfcert_howto.md) — running wolfCert's EST/SCEP client on wolfIP sockets (`WOLFCERT_WOLFIP`), the transport vtable, and name resolution.
- [HTTP/HTTPS server](http_server_howto.md) — the `src/http/` server module (`WOLFIP_ENABLE_HTTP`), handler registration, and enabling HTTPS via a `WOLFSSL_CTX`.
- [IPsec ESP how-to](ipsec_esp_howto.md) — build with `WOLFIP_ESP`, install Security Associations, and interoperate with Linux `ip xfrm`.
- [wolfGuard (FIPS WireGuard)](wolfguard_howto.md) — the in-stack WireGuard tunnel (`WOLFGUARD`), peer/key setup, and kernel interop.
Expand Down
187 changes: 187 additions & 0 deletions docs/wolfcert_howto.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# Certificate enrolment (wolfCert) How-To

wolfCert is wolfSSL's certificate enrolment library: it speaks EST (RFC 7030)
and SCEP (RFC 8894) to a CA and hands back an issued certificate. This guide
covers running it on wolfIP, so a device with no BSD sockets can enrol.

It is a getting-started document, not a reference manual. The authoritative
glue is `src/port/wolfcert_io.c` (declared in `wolfip.h` under
`WOLFCERT_WOLFIP`). The wolfCert API itself — `wolfcert_est_simple_enroll()`,
`wolfcert_scep_*`, key and CSR generation — is documented by wolfCert; this
guide only covers the wolfIP integration points.

## Table of Contents

- [1. What the integration provides](#1-what-the-integration-provides)
- [2. Building with wolfCert support](#2-building-with-wolfcert-support)
- [3. Registering the transport](#3-registering-the-transport)
- [4. The transport callbacks](#4-the-transport-callbacks)
- [5. Name resolution](#5-name-resolution)
- [6. Timeouts and the poll loop](#6-timeouts-and-the-poll-loop)
- [7. Troubleshooting](#7-troubleshooting)

---

## 1. What the integration provides

wolfCert opens its own connections, so it exposes a `WolfCertTransport` vtable
— `connect`, `read`, `write`, `disconnect` — that a stack without BSD sockets
fills in. `src/port/wolfcert_io.c` is wolfIP's implementation of that vtable,
in the same spirit as `src/port/wolfssl_io.c` for wolfSSL.

One transport carries every protocol wolfCert speaks:

| Deployment | Covered |
|---|---|
| EST over HTTPS | yes |
| SCEP over HTTPS | yes |
| SCEP over plain HTTP | yes |

TLS records travel through the same `read`/`write` as plain HTTP, so the rows
above are all carried by wolfIP end to end and the glue needs no TLS code of
its own.

`src/port/wolfssl_io.c` and `WOLFSSL_WOLFIP` are a different integration —
running wolfSSL directly on wolfIP sockets, see
[TLS over wolfIP](tls_howto.md). They are not needed here, and an application
can use both.

## 2. Building with wolfCert support

The integration is gated by **`WOLFCERT_WOLFIP`** and lives in one source
file, `src/port/wolfcert_io.c`, which you compile in and link against
`-lwolfcert` (and `-lwolfssl`, which wolfCert requires).

1. Build and install wolfCert first.
2. Compile `src/port/wolfcert_io.c` together with your application.
3. Add `-DWOLFCERT_WOLFIP` to the wolfIP/application `CFLAGS`, so the
declarations in `wolfip.h` are exposed.
4. Link with `-lwolfcert -lwolfssl`.

On a device with no sockets and no filesystem, wolfCert can also drop its own
POSIX transport and file store; see wolfCert's `docs/EMBEDDED.md` for those
build options.

When `WOLFCERT_WOLFIP` is defined, `wolfip.h` declares the two entry points:

```c
void *wolfCert_Init_wolfIP(WolfCertTransport *t, struct wolfIP *stack,
uint64_t (*now_ms)(void));
void wolfCert_Cleanup_wolfIP(void *context);
```

One compile-time knob, `MAX_WOLFCERT_CTX` (default 2, in
`src/port/wolfcert_io.c`), sizes the static context pool.

## 3. Registering the transport

`wolfCert_Init_wolfIP()` opens nothing. It fills in a transport you own and
returns a context handle for the matching cleanup call:

```c
static WolfCertTransport wc_transport;
static void *wc_io;

static uint64_t my_now_ms(void)
{
return board_get_tick(); /* the clock you already feed wolfIP_poll() */
}

wc_io = wolfCert_Init_wolfIP(&wc_transport, ipstack, my_now_ms);
if (wc_io == NULL)
return -1; /* bad arguments, or the pool is full */

cfg.transport = wc_transport; /* WolfCertServerCfg, WolfCertHttpSessionCfg
* or WolfCertHttpRequest */
```

Call `wolfCert_Cleanup_wolfIP(wc_io)` when you are done with the stack, to
release the pool slot.

`now_ms` returns milliseconds and must advance. It is the only clock the
transport has: it drives `wolfIP_poll()` while a connect or a blocking
transfer is in progress, and it bounds every timeout.

## 4. The transport callbacks

wolfCert calls these; your application does not. `read` and `write` map
wolfIP's return codes like this:

| `wolfIP_sock_recv`/`send` returns | Reported as |
|---|---|
| `> 0` | the byte count — short transfers are passed through |
| `0` (receive only) | `WOLFCERT_ERR_CONN_CLOSED` — the peer closed |
| `-1` | `WOLFCERT_ERR_CONN_CLOSED` — the socket is no longer established |
| `-WOLFIP_EAGAIN` | `WANT_READ`/`WANT_WRITE`, or poll and retry when blocking |
| `-WOLFIP_EINVAL` | `WOLFCERT_ERR_BAD_ARG` |
| anything else | `WOLFCERT_ERR_IO` |

`connect` does not use this mapping. It reports `WOLFCERT_ERR_BAD_ARG` for a
host or port it will not accept, `WOLFCERT_ERR_NOT_FOUND` when a name resolves
to no address, and `WOLFCERT_ERR_IO` for every other failure.

The connection handle is the wolfIP descriptor, handed back as an opaque
pointer. `disconnect` closes it and polls until the FIN handshake finishes,
since `wolfIP_sock_close()` reports `-WOLFIP_EAGAIN` until then.

## 5. Name resolution

`connect` accepts either a dotted quad or a hostname. A dotted quad is parsed
locally; a hostname goes to `nslookup()` and the answer is awaited inside
`connect`.

**Only one hostname lookup runs at a time, even if you created several
transport contexts.** `nslookup()`'s callback carries no user pointer, so the
transport collects the answer in a single slot. That matches wolfIP's own
one-query-per-stack limit, and `connect` runs to completion before returning,
so lookups cannot overlap.

Two behaviours of the resolver shape what a failed lookup costs here — a name
that does not exist is never reported, only timed out, and an abandoned query
clears on its own schedule rather than being cancelled. Both are described in
[DHCP & DNS clients](dhcp_dns_howto.md); the practical effect is that a failed
resolution spends the connect budget, and retrying at once can spend part of
the next one.

Prefer keeping the hostname in the URL over an IP literal, so the server
certificate is verified against the name.

## 6. Timeouts and the poll loop

`connect`, and any blocking `read`/`write`, drive `wolfIP_poll()` themselves —
nothing else runs during that call. Two defaults bound them when the caller
sets no timeout, both overridable at compile time:

| Macro | Default | Applies to |
|---|---|---|
| `WOLFCERT_WOLFIP_CONNECT_TIMEOUT_MS` | 30000 | `connect`, including resolution |
| `WOLFCERT_WOLFIP_IO_TIMEOUT_MS` | 30000 | blocking `read`/`write`, and `disconnect` |

**Set `cfg.timeout_ms` explicitly on an MCU.** A value comfortably inside your
watchdog period — a few seconds — is the right choice, because the application
is stalled for the whole of a connect. A refused connection costs the full
budget too: wolfIP reports a reset socket the same way it reports a handshake
still in progress, so the transport cannot fail early on an RST.

Where wolfCert offers a non-blocking mode, prefer it: `read` and `write` then
return immediately instead of polling internally, leaving your own loop to
pace the stack, as in [TLS over wolfIP](tls_howto.md) section 8. The connect
is synchronous either way.

## 7. Troubleshooting

**Link errors on `wolfCert_Init_wolfIP`.** `src/port/wolfcert_io.c` was not
compiled, or `-DWOLFCERT_WOLFIP` was not passed. See section 2.

**`wolfCert_Init_wolfIP()` returns NULL.** A NULL argument, or more transports
than `MAX_WOLFCERT_CTX`.

**Requests fail immediately with a bad-argument error.** The config carries no
transport, or the URL's host is empty.

**Connects always take the full timeout.** The peer is unreachable or
refusing. Check the route and that `now_ms` actually advances — a clock that
never moves leaves the transport polling with no deadline.

**EST enrolment fails with a TLS error.** EST requires the client to
authenticate the server; check wolfCert's trust-anchor settings.
Loading
Loading