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
21 changes: 17 additions & 4 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,15 +181,28 @@ whose payload is an EnvelopedData, built on wolfSSL's `wc_PKCS7` API.

Each round trip envelopes the payload to the RA/CA cert's public key, signs it
(for `PKCSReq` with a transient self-signed cert whose key matches the one
being enrolled), POSTs it, and parses the response. The server's `pkiStatus`
maps to a `WolfCertScepResult.status` of `SUCCESS` (cert in `cert_pem`),
`PENDING` (poll with `GetCertInitial`, quoting the returned transaction ID),
or `FAILURE`.
being enrolled), sends it, and parses the response. The pkiMessage is POSTed by
default; when the passed `caps` shows the CA does **not** advertise
`POSTPKIOperation`, the client falls back to the RFC 8894 section 4.1 HTTP GET
form, carrying the message base64-encoded and percent-escaped in the `message`
query parameter (refusing, with `WOLFCERT_ERR_UNSUPPORTED`, to build a URL
longer than `WOLFCERT_SCEP_MAX_GET_URL`). The in-tree test server accepts both.
The server's `pkiStatus` maps to a `WolfCertScepResult.status` of `SUCCESS`
(cert in `cert_pem`), `PENDING` (poll with `GetCertInitial`, quoting the
returned transaction ID), or `FAILURE`.

**SCEP is RSA-only.** The entry points reject non-RSA keys with
`WOLFCERT_ERR_UNSUPPORTED` — this is a protocol constraint, not a wolfCert
limitation. Use EST for Ed25519 / Ed448 / ML-DSA.

**Trust bootstrap.** A `GetCACert` response is only trustworthy once its
fingerprint has been checked against a value obtained out of band.
`wolfcert_scep_verify_ca_fingerprint` hashes the DER CA certificate (SHA-256,
or SHA-1 / SHA-512, with `WOLFCERT_SCEP_FP_AUTO` selecting the algorithm from
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.

**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
26 changes: 17 additions & 9 deletions docs/EMBEDDED.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ miscompile.
|----------|----------------|--------------|--------------|
| wolfSSL `Cert` (CSR / cert build) | **heap** (`wc_CertNew`) | ~20+ KB | `altNames[16384]` |
| wolfSSL `DecodedCert` (cert parse) | **stack**, transient | several KB | parse scratch |
| HTTP request handling | **stack** | 2-3 KB | request read buffer |
| HTTP request handling | **stack** (EST + client); **heap** (SCEP server) | 2-3 KB stack | request read buffer |

The good news: wolfCert never stack-allocates a `Cert`. Every CSR/cert
build path (`src/csr.c`, `src/ca_issue.c`, `src/scep/scep_msg.c`) obtains
Expand Down Expand Up @@ -121,24 +121,32 @@ Trade-offs:
## 2. wolfCert HTTP stack buffers

The in-tree test server and HTTP client size a few request-handling
buffers on the stack. They are tunable with `#ifndef`-guarded macros in
`src/internal.h`; override via `-D` or your `user_settings`-style config
header.
buffers with `#ifndef`-guarded macros in `src/internal.h`; override via `-D`
or your `user_settings`-style config header. The EST server and the client
place these on the stack; the SCEP server, whose GET `PKIOperation` read
buffer is by far the largest, allocates it on the **heap** (freed as soon as
the request completes) so it never counts against the stack budget.

| Macro | Default | Buffer |
|-------|---------|--------|
| `WOLFCERT_HTTP_REQ_BUF_SZ` | `2048` | server request-header read buffer (`est_server.c`, `scep_server.c`) |
| `WOLFCERT_HTTP_PATH_SZ` | `512` | server request `path` / `query` fields; the SCEP `full` reconstruction buffer is `2 ×` this |
| `WOLFCERT_HTTP_REQ_BUF_SZ` | `2048` | server request-header read buffer. The SCEP server adds `WOLFCERT_HTTP_QUERY_SZ` to it (so a base64 GET `PKIOperation` fits) and allocates the result on the heap; the EST server keeps its `2048`-byte buffer on the stack (`est_server.c`, `scep_server.c`) |
| `WOLFCERT_HTTP_PATH_SZ` | `512` | EST server request `path` field (the SCEP server points `path`/`query` into its heap read buffer instead) |
| `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_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` lowers the longest
request path/query; `WOLFCERT_HTTP_AUTH_BUF_SZ` lowers the longest
Basic-auth credential the client can send. Example:
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
can trim `WOLFCERT_HTTP_QUERY_SZ` (and, on the client, `WOLFCERT_SCEP_MAX_GET_URL`
and `WOLFCERT_HTTP_MAX_PATH_LEN`) back down. Example:

```c
#define WOLFCERT_HTTP_REQ_BUF_SZ 768
#define WOLFCERT_HTTP_PATH_SZ 128
#define WOLFCERT_HTTP_QUERY_SZ 256
#define WOLFCERT_HTTP_AUTH_BUF_SZ 128
```

Expand Down
10 changes: 4 additions & 6 deletions src/est/est_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <wolfssl/options.h>
#include <wolfssl/ssl.h>
#include <wolfssl/wolfcrypt/sha256.h>
#include <wolfssl/wolfcrypt/memory.h>

#include <arpa/inet.h>
#include <ctype.h>
Expand Down Expand Up @@ -520,12 +521,9 @@ static int check_basic_auth(const WolfCertServer* s, const char* auth_header)
* does not leak the length of the matching prefix. */
const char* tok = auth_header + EST_BASIC_AUTH_SCHEME_LEN;
int ok = (strlen(tok) == enc.len);
if (ok) {
unsigned acc = 0;
for (size_t i = 0; i < enc.len; ++i)
acc |= (unsigned)((unsigned char)tok[i] ^ enc.data[i]);
ok = (acc == 0);
}
if (ok)
ok = (wc_ConstantCompare((const byte*)tok, enc.data,
(int)enc.len) == 0);
wolfcert_buffer_free(&enc);

return ok;
Expand Down
8 changes: 7 additions & 1 deletion src/http.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@
#include <unistd.h>

#define WOLFCERT_HTTP_MAX_HOST_LEN 256
#define WOLFCERT_HTTP_MAX_PATH_LEN 2048
/* Large enough to carry an RFC 8894 GET PKIOperation whose base64 pkiMessage
* rides in the query string (bounded by WOLFCERT_SCEP_MAX_GET_URL). The parsed
* path is heap-allocated and the request head buffer is sized to it, so this is
* only a sanity ceiling. */
#ifndef WOLFCERT_HTTP_MAX_PATH_LEN
#define WOLFCERT_HTTP_MAX_PATH_LEN 8192
#endif
#define WOLFCERT_HTTP_DEFAULT_MAX_BODY (64 * 1024)
#define WOLFCERT_HTTP_READ_CHUNK 2048

Expand Down
42 changes: 35 additions & 7 deletions src/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,26 @@
#include <wolfssl/wolfcrypt/random.h>
#include <wolfssl/ssl.h>

/* ---- tunable stack-buffer sizes ----------------------------------------- *
* These bound the HTTP request-handling stack footprint of the test server
* and client. Override them (compiler -D or user_settings) to shrink stack
* usage on constrained targets; see docs/EMBEDDED.md. Shrinking lowers the
* largest request header block / path / Basic-auth credential accepted. */
/* ---- tunable HTTP request-buffer sizes ---------------------------------- *
* These bound the HTTP request-handling buffers of the test server and client.
* The EST server and the client keep them on the stack; the SCEP server
* heap-allocates its (larger) read buffer, so shrinking these lowers its heap
* churn rather than its stack. Override them (compiler -D or user_settings) on
* constrained targets; see docs/EMBEDDED.md. Shrinking lowers the largest
* request header block / path / Basic-auth credential accepted. */
#ifndef WOLFCERT_HTTP_REQ_BUF_SZ
#define WOLFCERT_HTTP_REQ_BUF_SZ 2048 /* server request header read buffer */
#endif
#ifndef WOLFCERT_HTTP_PATH_SZ
#define WOLFCERT_HTTP_PATH_SZ 512 /* server request path / query field */
#define WOLFCERT_HTTP_PATH_SZ 512 /* EST server request path field */
#endif
/* The query size is separate from the path: an RFC 8894 GET PKIOperation
* carries the whole base64/percent-encoded pkiMessage in the `message=` query
* parameter, so the buffer must be large enough to hold one. The SCEP server
* adds this to REQ_BUF_SZ for its heap read buffer (query points into it);
* trim it on a POST-only deployment to reclaim that buffer. */
#ifndef WOLFCERT_HTTP_QUERY_SZ
#define WOLFCERT_HTTP_QUERY_SZ 8192 /* SCEP GET pkiMessage query capacity */
#endif
#ifndef WOLFCERT_HTTP_AUTH_BUF_SZ
#define WOLFCERT_HTTP_AUTH_BUF_SZ 512 /* client Basic-auth header line */
Expand Down Expand Up @@ -86,6 +96,17 @@
#define WOLFCERT_SCEP_MAX_MSG_SZ (64 * 1024)
#endif

/* Upper bound on the length of a base64/percent-encoded PKIOperation GET URL.
* RFC 8894 section 4.1 lets a client fall back to HTTP GET when the CA does not
* advertise POSTPKIOperation, carrying the pkiMessage in the `message` query
* parameter. GET has no standard length limit but servers commonly cap request
* lines around 8 KiB, so refuse to build a longer URL and surface a clear
* error instead (a large message needs a POSTPKIOperation-capable CA). Override
* with a compiler -D or user_settings if a deployment allows longer GETs. */
#ifndef WOLFCERT_SCEP_MAX_GET_URL
#define WOLFCERT_SCEP_MAX_GET_URL (8 * 1024)
#endif

/* ---- key ---------------------------------------------------------------- */

struct WolfCertKey {
Expand Down Expand Up @@ -305,6 +326,13 @@ int wolfcert_scep_issuer_and_subject(const uint8_t* issuer_cert_der, size_t issu
WOLFCERT_TEST_VIS int wolfcert_scep_envelop(const uint8_t* ra_cert_der,
size_t ra_cert_len, const uint8_t* payload, size_t payload_len, int enc_oid,
WolfCertBuffer* out_der, void* heap);

/* Build the RFC 8894 section 4.1 GET PKIOperation URL:
* base?operation=PKIOperation&message=<url-encoded base64 pki_msg>
* Returns WOLFCERT_ERR_UNSUPPORTED when the encoded URL would exceed
* WOLFCERT_SCEP_MAX_GET_URL. Exposed for white-box testing. */
WOLFCERT_TEST_VIS int wolfcert_scep_build_pki_get_url(const char* base,
const uint8_t* pki_msg, size_t pki_len, void* heap, char** out_url);
int wolfcert_scep_deenvelop(const uint8_t* recipient_cert_der, size_t recipient_cert_len,
const uint8_t* recipient_key_der, size_t recipient_key_len,
const uint8_t* env_der, size_t env_len,
Expand All @@ -322,7 +350,7 @@ WOLFCERT_TEST_VIS int wolfcert_scep_parse_pki_message(const uint8_t* pki_der,
size_t* out_tid_len, uint8_t** out_sender_nonce, size_t* out_snonce_len,
uint8_t** out_recipient_nonce,size_t* out_rnonce_len, char** out_message_type,
char** out_pki_status, uint8_t** out_signer_cert, size_t* out_signer_cert_len,
void* heap);
char** out_fail_info, void* heap);

/* Build the GetNextCACert response (RFC 8894 section 4.6.1): wrap the next CA
* certificate in a degenerate certs-only SignedData and sign that with the
Expand Down
Loading
Loading