From a49b2b00ff911fe6ba492ed93fc5e9c16a4dacf1 Mon Sep 17 00:00:00 2001 From: Yosuke Shimizu Date: Mon, 19 Oct 2026 15:40:00 +0900 Subject: [PATCH 1/5] est: one request builder, response parser and session call - est_finish() parses every EST response; 401/403 returns WOLFCERT_ERR_AUTH from every operation, and csrattrs reads an empty body as no attributes only on 200, 204, 404, 405 or 501; est.h, types.h and ARCHITECTURE.md say so. - est_session_call() runs every session call; est_one_shot() opens a session, makes one call and closes it for every one-shot. - est_enroll_finish(), fill_common(), post_enroll_ex(), est_result_flatten() and est_check_cfg() are removed. - New session calls: get_cacerts_enc and get_csr_attrs with _nb forms, and simple_reenroll_ex with an _nb_ex form. - One-shots read proto_opts.est.allow_post_handshake_auth too; types.h no longer limits it to the session API. - test_est_pha_roundtrip reenrolls over PHA through the session call in place of a raw TLS client. - wolfcert_http_session_set_close() makes an HTTP session send Connection: close; wolfcert_http_request() and est_one_shot() call it. - est_session_free_request() frees the request URL and body once the HTTP call succeeds, before est_finish() parses the response. - types.h: allow_post_handshake_auth applies to every EST call. - A session reenroll without a client identity returns WOLFCERT_ERR_BAD_ARG before it sends. - wolfcert_est_session_open() hands its URL to the HTTP session, and http.h says a base_url path is ignored. --- docs/ARCHITECTURE.md | 2 +- src/est/est_client.c | 850 +++++++----------- src/http.c | 8 +- src/internal.h | 3 + .../test_est_csr_attrs_roundtrip.c | 27 + tests/integration/test_est_mtls_roundtrip.c | 41 + tests/integration/test_est_pha_roundtrip.c | 96 +- tests/integration/test_est_roundtrip.c | 11 + tests/unit/test_est.c | 55 +- wolfcert/est.h | 32 +- wolfcert/http.h | 3 +- wolfcert/types.h | 10 +- 12 files changed, 542 insertions(+), 596 deletions(-) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index c2a8609..a8ecefc 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -138,7 +138,7 @@ four endpoints a typical device needs: | Endpoint | Entry point | Notes | |---|---|---| | `GET /cacerts` | `wolfcert_est_get_cacerts` | CA chain as degenerate PKCS#7; decoded to PEM for you. | -| `GET /csrattrs` | `wolfcert_est_get_csr_attrs` | Raw body (empty on HTTP 204); decode with `wolfcert_est_parse_csr_attrs`. | +| `GET /csrattrs` | `wolfcert_est_get_csr_attrs` | Raw body (empty on HTTP 204, 404, 405, 501 or an empty 200); decode with `wolfcert_est_parse_csr_attrs`. | | `POST /simpleenroll` | `wolfcert_est_simple_enroll` | Body is base64-wrapped CSR DER; 200 returns the issued cert as PKCS#7. | | `POST /simplereenroll` | `wolfcert_est_simple_reenroll` | Same, with the cert being renewed used as the implicit client identity. The test server's requirements for it are at `WolfCertServerCfgSrv.tls_client_ca_pem` in `wolfcert/server.h`. | diff --git a/src/est/est_client.c b/src/est/est_client.c index d7926fa..58982c0 100644 --- a/src/est/est_client.c +++ b/src/est/est_client.c @@ -29,6 +29,28 @@ #include #include +enum est_session_op { + EST_OP_GET_CACERTS, + EST_OP_GET_CSRATTRS, + EST_OP_SIMPLE_ENROLL, + EST_OP_SIMPLE_REENROLL +}; + +static const struct { + const char* path; + const char* accept; +} est_ops[] = { + { "cacerts", "application/pkcs7-mime" }, + { "csrattrs", "application/csrattrs" }, + { "simpleenroll", "application/pkcs7-mime" }, + { "simplereenroll", "application/pkcs7-mime" } +}; + +static int est_is_enroll(int op) +{ + return op == EST_OP_SIMPLE_ENROLL || op == EST_OP_SIMPLE_REENROLL; +} + static char* join_path(const char* base, const char* suffix, void* heap) { size_t bl = strlen(base); @@ -46,207 +68,63 @@ static char* join_path(const char* base, const char* suffix, void* heap) return out; } -static void fill_common(const WolfCertServerCfg* srv, WolfCertHttpRequest* req) -{ - req->basic_user = srv->proto_opts.est.username; - req->basic_pass = srv->proto_opts.est.password; - req->trust_anchors = srv->trust_anchors; - req->trust_anchors_len = srv->trust_anchors_len; - req->verify_server = srv->verify_server; - req->timeout_ms = srv->timeout_ms; - req->max_response_bytes= srv->max_response_bytes; - req->heap = srv->heap; - - /* mTLS identity, if the caller supplied one */ - req->client_cert = srv->client_cert; - req->client_cert_len = srv->client_cert_len; - req->client_key = srv->client_key; - req->client_key_len = srv->client_key_len; - req->transport = srv->transport; -} - -/* The protocol check comes first because proto_opts is a union. RFC 7030 - * requires TLS and server authentication; verify_server alone turns on peer - * verification, so trust_anchors are not enforced without it. */ -static int est_check_cfg(const WolfCertServerCfg* srv, void* heap) +/* Parse the response to op into *out and free resp. res takes an enroll op's + * status and is NULL for the other ops. */ +static int est_finish(void* heap, WolfCertHttpResponse* resp, int op, + WolfCertEncoding enc, WolfCertBuffer* out, + WolfCertEstResult* res) { - WolfCertUrl u; - int rc = wolfcert_cfg_require_proto(srv, WOLFCERT_PROTO_EST, "est"); - if (rc != WOLFCERT_OK) - return rc; - - rc = wolfcert_http_url_parse(srv->server_url, &u, heap); - if (rc != WOLFCERT_OK) - return rc; - - int tls = u.tls; - wolfcert_http_url_free(&u); - - if (!tls) - return WOLFCERT_ERR(WOLFCERT_ERR_TLS, "est", - "EST requires TLS; refusing plaintext http:// URL (RFC 7030)"); - - if (!srv->verify_server) - return WOLFCERT_ERR(WOLFCERT_ERR_TLS, "est", - "EST requires server authentication: set verify_server to verify " - "the server certificate (RFC 7030)"); - - return WOLFCERT_OK; -} - -int wolfcert_est_get_cacerts(const WolfCertServerCfg* srv, WolfCertBuffer* out_ca_pem) -{ - return wolfcert_est_get_cacerts_enc(srv, WOLFCERT_ENCODING_PEM, out_ca_pem); -} - -int wolfcert_est_get_cacerts_enc(const WolfCertServerCfg* srv, WolfCertEncoding enc, - WolfCertBuffer* out_ca) -{ - if (srv == NULL || srv->server_url == NULL || out_ca == NULL) - return WOLFCERT_ERR_BAD_ARG; - - void* heap = srv->heap ? srv->heap : wolfcert_default_heap(); - int trc = est_check_cfg(srv, heap); - if (trc != WOLFCERT_OK) - return trc; - - char* url = join_path(srv->server_url, "cacerts", heap); - if (url == NULL) - return WOLFCERT_ERR_MEMORY; - - WolfCertHttpRequest req = { .method = "GET", .url = url, - .accept = "application/pkcs7-mime" }; - fill_common(srv, &req); - - WolfCertHttpResponse resp = { 0 }; - int rc = wolfcert_http_request(&req, &resp); - - WOLFCERT_XFREE(url, heap); - if (rc != WOLFCERT_OK) - return rc; - - if (resp.status_code != 200) { - wolfcert_http_response_free(&resp); - return WOLFCERT_ERR_HTTP; - } - - if (resp.body_len == 0) { - wolfcert_http_response_free(&resp); - return WOLFCERT_ERR(WOLFCERT_ERR_HTTP, "est", - "200 response carried no certificate"); - } - - WolfCertBuffer p7 = { 0 }; - rc = wolfcert_base64_decode(resp.body, resp.body_len, &p7, heap); - - wolfcert_http_response_free(&resp); - if (rc != WOLFCERT_OK) - return rc; - - if (enc == WOLFCERT_ENCODING_DER) - rc = wolfcert_pkcs7_certs_to_der(p7.data, p7.len, out_ca, heap); - else - rc = wolfcert_pkcs7_certs_to_pem(p7.data, p7.len, out_ca, heap); - - wolfcert_buffer_free(&p7); - return rc; -} - -/* Fill *out from a /simpleenroll or /simplereenroll response, and free resp. */ -static int est_enroll_finish(void* heap, WolfCertHttpResponse* resp, - WolfCertEstResult* out) -{ - int rc; + int rc = WOLFCERT_OK; int status = resp->status_code; - WolfCertBuffer p7 = { 0 }; + int decoded = 0; + WolfCertBuffer der = { 0 }; /* RFC 7030 section 4.2.3: 202 Accepted = enrolment pending. */ - if (status == 202) { - out->status = WOLFCERT_EST_STATUS_PENDING; - out->retry_after_sec = resp->retry_after_sec; + if (res != NULL && status == 202) { + res->status = WOLFCERT_EST_STATUS_PENDING; + res->retry_after_sec = resp->retry_after_sec; + } + /* No CSR attributes (RFC 7030 section 4.5.2). */ + else if (op == EST_OP_GET_CSRATTRS && + (status == 204 || status == 404 || status == 405 || + status == 501 || (status == 200 && resp->body_len == 0))) { rc = WOLFCERT_OK; } else if (status != 200) { - out->status = WOLFCERT_EST_STATUS_FAILURE; + if (res != NULL) + res->status = WOLFCERT_EST_STATUS_FAILURE; rc = WOLFCERT_ERR(status == 401 || status == 403 ? WOLFCERT_ERR_AUTH : WOLFCERT_ERR_HTTP, - "est", "enrollment rejected: HTTP %d", status); + "est", "%s rejected: HTTP %d", est_ops[op].path, + status); } else if (resp->body_len == 0) { rc = WOLFCERT_ERR(WOLFCERT_ERR_HTTP, "est", "200 response carried no certificate"); } else { - rc = wolfcert_base64_decode(resp->body, resp->body_len, &p7, heap); + rc = wolfcert_base64_decode(resp->body, resp->body_len, &der, heap); + decoded = (rc == WOLFCERT_OK); } wolfcert_http_response_free(resp); - if (rc == WOLFCERT_OK && status == 200) { - rc = wolfcert_pkcs7_certs_to_pem(p7.data, p7.len, &out->cert_pem, heap); - if (rc == WOLFCERT_OK) - out->status = WOLFCERT_EST_STATUS_SUCCESS; + if (decoded && op == EST_OP_GET_CSRATTRS) { + *out = der; + memset(&der, 0, sizeof(der)); } - wolfcert_buffer_free(&p7); - return rc; -} - -/* Round-trip for /simpleenroll and /simplereenroll. */ -static int post_enroll_ex(const WolfCertServerCfg* srv, - const char* suffix, - const uint8_t* csr_der, size_t csr_der_len, - const uint8_t* renew_cert_pem, size_t renew_cert_len, - const uint8_t* renew_key_pem, size_t renew_key_len, - WolfCertEstResult* out) -{ - void* heap = srv->heap ? srv->heap : wolfcert_default_heap(); - memset(out, 0, sizeof(*out)); - out->heap = heap; - int trc = est_check_cfg(srv, heap); - if (trc != WOLFCERT_OK) - return trc; - - char* url = join_path(srv->server_url, suffix, heap); - if (url == NULL) - return WOLFCERT_ERR_MEMORY; - - /* RFC 7030 section 3.5 tls-unique channel binding is not implemented. */ - - WolfCertBuffer b64 = { 0 }; - int rc = wolfcert_base64_encode_mime(csr_der, csr_der_len, &b64, heap); - if (rc != WOLFCERT_OK) { - WOLFCERT_XFREE(url, heap); - return rc; + else if (decoded && enc == WOLFCERT_ENCODING_DER) { + rc = wolfcert_pkcs7_certs_to_der(der.data, der.len, out, heap); } - - WolfCertHttpRequest req = { - .method = "POST", - .url = url, - .content_type = "application/pkcs10", - .content_transfer_encoding = "base64", - .accept = "application/pkcs7-mime", - .body = b64.data, - .body_len = b64.len, - }; - fill_common(srv, &req); - - /* simplereenroll authenticates TLS with the cert being renewed */ - if (renew_cert_pem != NULL) { - req.client_cert = renew_cert_pem; - req.client_cert_len = renew_cert_len; - req.client_key = renew_key_pem; - req.client_key_len = renew_key_len; + else if (decoded) { + rc = wolfcert_pkcs7_certs_to_pem(der.data, der.len, out, heap); } - WolfCertHttpResponse resp = { 0 }; - rc = wolfcert_http_request(&req, &resp); - - WOLFCERT_XFREE(url, heap); - wolfcert_buffer_free(&b64); - if (rc != WOLFCERT_OK) - return rc; + if (decoded && rc == WOLFCERT_OK && res != NULL) + res->status = WOLFCERT_EST_STATUS_SUCCESS; - return est_enroll_finish(heap, &resp, out); + wolfcert_buffer_free(&der); + return rc; } void wolfcert_est_result_free(WolfCertEstResult* r) @@ -260,116 +138,24 @@ void wolfcert_est_result_free(WolfCertEstResult* r) r->heap = NULL; } -/* Flatten an _ex result for the simple-result calls: PENDING becomes - * WOLFCERT_ERR_PENDING and an issued cert moves to out_cert_pem. Frees r. */ -static int est_result_flatten(int rc, WolfCertEstResult* r, - WolfCertBuffer* out_cert_pem) -{ - if (rc == WOLFCERT_OK && r->status == WOLFCERT_EST_STATUS_PENDING) { - rc = WOLFCERT_ERR_PENDING; - } - else if (rc == WOLFCERT_OK) { - *out_cert_pem = r->cert_pem; - r->cert_pem.data = NULL; - r->cert_pem.len = 0; - } - - wolfcert_est_result_free(r); - return rc; -} - -int wolfcert_est_simple_enroll_ex(const WolfCertServerCfg* srv, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertEstResult* out) -{ - if (out == NULL) - return WOLFCERT_ERR_BAD_ARG; - - memset(out, 0, sizeof(*out)); - - if (srv == NULL || csr_der == NULL || csr_der_len == 0) - return WOLFCERT_ERR_BAD_ARG; - - return post_enroll_ex(srv, "simpleenroll", csr_der, csr_der_len, - NULL, 0, NULL, 0, out); -} - -int wolfcert_est_simple_reenroll_ex(const WolfCertServerCfg* srv, - const uint8_t* current_cert, size_t current_cert_len, - const WolfCertKey* current_key, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertEstResult* out) -{ - if (out == NULL) - return WOLFCERT_ERR_BAD_ARG; - - memset(out, 0, sizeof(*out)); - - if (srv == NULL || current_cert == NULL || current_key == NULL || - csr_der == NULL) - return WOLFCERT_ERR_BAD_ARG; - - WolfCertBuffer key_pem = { 0 }; - int rc = wolfcert_key_to_pem(current_key, &key_pem); - if (rc != WOLFCERT_OK) - return rc; - - rc = post_enroll_ex(srv, "simplereenroll", csr_der, csr_der_len, - current_cert, current_cert_len, - key_pem.data, key_pem.len, out); - - wc_ForceZero(key_pem.data, (word32)key_pem.len); - wolfcert_buffer_free(&key_pem); - return rc; -} - -int wolfcert_est_simple_enroll(const WolfCertServerCfg* srv, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertBuffer* out_cert_pem) -{ - if (srv == NULL || csr_der == NULL || csr_der_len == 0 || out_cert_pem == NULL) - return WOLFCERT_ERR_BAD_ARG; - - WolfCertEstResult r = { 0 }; - int rc = post_enroll_ex(srv, "simpleenroll", csr_der, csr_der_len, - NULL, 0, NULL, 0, &r); - return est_result_flatten(rc, &r, out_cert_pem); -} - -int wolfcert_est_simple_reenroll(const WolfCertServerCfg* srv, - const uint8_t* current_cert, size_t current_cert_len, - const WolfCertKey* current_key, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertBuffer* out_cert_pem) -{ - if (srv == NULL || current_cert == NULL || current_key == NULL || - csr_der == NULL || out_cert_pem == NULL) - return WOLFCERT_ERR_BAD_ARG; - - WolfCertEstResult r = { 0 }; - int rc = wolfcert_est_simple_reenroll_ex(srv, current_cert, current_cert_len, - current_key, csr_der, csr_der_len, &r); - return est_result_flatten(rc, &r, out_cert_pem); -} - struct WolfCertEstSession { WolfCertHttpSession* http; char* base_url; /* e.g. https://ca.example/.well-known/est */ - size_t max_body; void* heap; /* Copied at open so the cfg need not outlive the session; NULL if none. */ char* basic_user; char* basic_pass; + int has_client_identity; - /* Async in-flight state: at most one request at a time. */ + /* The request in flight, blocking or _nb; at most one at a time. */ int in_active; char* in_url; WolfCertBuffer in_body; /* base64-encoded CSR for POST, owned */ WolfCertHttpRequest in_req; WolfCertHttpResponse in_resp; const void* in_owner; /* caller's output, checked on resume */ - enum { EST_OP_GET_CACERTS, EST_OP_SIMPLE_ENROLL } in_op; + enum est_session_op in_op; }; static int est_session_open_common(const WolfCertServerCfg* srv, @@ -384,44 +170,33 @@ static int est_session_open_common(const WolfCertServerCfg* srv, if (rc != WOLFCERT_OK) return rc; - /* The HTTP session gets the origin; endpoint paths join onto base_url. */ WolfCertUrl u; rc = wolfcert_http_url_parse(srv->server_url, &u, heap); if (rc != WOLFCERT_OK) return rc; - if (!u.tls) { - wolfcert_http_url_free(&u); + int tls = u.tls; + wolfcert_http_url_free(&u); + if (!tls) return WOLFCERT_ERR(WOLFCERT_ERR_TLS, "est", "EST requires TLS; refusing plaintext http:// URL (RFC 7030)"); - } - if (!srv->verify_server) { - wolfcert_http_url_free(&u); + if (!srv->verify_server) return WOLFCERT_ERR(WOLFCERT_ERR_TLS, "est", "EST requires server authentication: set verify_server to verify " "the server certificate (RFC 7030)"); - } - - 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) { - WOLFCERT_XFREE(origin, heap); + if (s == NULL) return WOLFCERT_ERR_MEMORY; - } memset(s, 0, sizeof(*s)); - s->heap = heap; - s->base_url = wolfcert_strdup(srv->server_url, heap); - s->max_body = srv->max_response_bytes; + s->heap = heap; + s->has_client_identity = srv->client_cert != NULL && + srv->client_key != NULL; + s->base_url = wolfcert_strdup(srv->server_url, heap); if (s->base_url == NULL) { WOLFCERT_XFREE(s, heap); - WOLFCERT_XFREE(origin, heap); return WOLFCERT_ERR_MEMORY; } @@ -434,12 +209,11 @@ static int est_session_open_common(const WolfCertServerCfg* srv, if ((user != NULL && s->basic_user == NULL) || (pass != NULL && s->basic_pass == NULL)) { wolfcert_est_session_close(s); - WOLFCERT_XFREE(origin, heap); return WOLFCERT_ERR_MEMORY; } WolfCertHttpSessionCfg hcfg = { - .base_url = origin, + .base_url = s->base_url, .trust_anchors = srv->trust_anchors, .trust_anchors_len = srv->trust_anchors_len, .verify_server = srv->verify_server, @@ -454,8 +228,6 @@ static int est_session_open_common(const WolfCertServerCfg* srv, .heap = heap, }; rc = wolfcert_http_session_open(&hcfg, &s->http); - - WOLFCERT_XFREE(origin, heap); if (rc != WOLFCERT_OK) { /* Free with wolfcert_est_session_close() to zero s->basic_pass. */ wolfcert_est_session_close(s); @@ -483,13 +255,19 @@ int wolfcert_est_session_fd(const WolfCertEstSession* s) return s != NULL ? wolfcert_http_session_fd(s->http) : -1; } -static void est_async_reset(WolfCertEstSession* s) +/* Free the request in flight; its response stays for the parse. */ +static void est_session_free_request(WolfCertEstSession* s) { WOLFCERT_XFREE(s->in_url, s->heap); s->in_url = NULL; wolfcert_buffer_free(&s->in_body); + memset(&s->in_req, 0, sizeof(s->in_req)); +} + +static void est_async_reset(WolfCertEstSession* s) +{ + est_session_free_request(s); wolfcert_http_response_free(&s->in_resp); - memset(&s->in_req, 0, sizeof(s->in_req)); memset(&s->in_resp, 0, sizeof(s->in_resp)); s->in_owner = NULL; s->in_active = 0; @@ -529,123 +307,163 @@ void wolfcert_est_session_close(WolfCertEstSession* s) WOLFCERT_XFREE(s, s->heap); } -int wolfcert_est_session_get_cacerts_nb(WolfCertEstSession* s, - WolfCertBuffer* out_ca_pem) +/* Build the request for op into s->in_* and mark it in flight. */ +static int est_session_begin(WolfCertEstSession* s, int op, + const uint8_t* csr_der, size_t csr_der_len, + const void* owner) { - int rc; + int rc = WOLFCERT_OK; - if (s == NULL || out_ca_pem == NULL) - return WOLFCERT_ERR_BAD_ARG; + s->in_url = join_path(s->base_url, est_ops[op].path, s->heap); + if (s->in_url == NULL) + rc = WOLFCERT_ERR_MEMORY; - if (!s->in_active) { - s->in_url = join_path(s->base_url, "cacerts", s->heap); - if (s->in_url == NULL) - return WOLFCERT_ERR_MEMORY; + if (rc == WOLFCERT_OK && est_is_enroll(op)) { + rc = wolfcert_base64_encode_mime(csr_der, csr_der_len, &s->in_body, + s->heap); + } + if (rc == WOLFCERT_OK) { s->in_req = (WolfCertHttpRequest){ - .method = "GET", .url = s->in_url, - .accept = "application/pkcs7-mime", - .basic_user = s->basic_user, .basic_pass = s->basic_pass, - .max_response_bytes = s->max_body, - .heap = s->heap, + .method = "GET", + .url = s->in_url, + .accept = est_ops[op].accept, + .basic_user = s->basic_user, + .basic_pass = s->basic_pass, + .heap = s->heap, }; - s->in_owner = out_ca_pem; - s->in_op = EST_OP_GET_CACERTS; + if (est_is_enroll(op)) { + s->in_req.method = "POST"; + s->in_req.content_type = "application/pkcs10"; + s->in_req.content_transfer_encoding = "base64"; + s->in_req.body = s->in_body.data; + s->in_req.body_len = s->in_body.len; + } + s->in_op = (enum est_session_op)op; + s->in_owner = owner; s->in_active = 1; } else { - rc = est_session_resume_check(s, EST_OP_GET_CACERTS, out_ca_pem); - if (rc != WOLFCERT_OK) - return rc; + est_async_reset(s); } - rc = wolfcert_http_session_request_nb(s->http, &s->in_req, &s->in_resp); - if (rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE) - return rc; + return rc; +} - if (rc != WOLFCERT_OK) { - est_async_reset(s); +/* Run op on s, or continue the _nb request in flight, and parse the response + * into *out and, for an enroll op, *res. *out also identifies the caller + * across _nb calls. */ +static int est_session_call(WolfCertEstSession* s, int op, + const uint8_t* csr_der, size_t csr_der_len, + WolfCertEncoding enc, WolfCertBuffer* out, + WolfCertEstResult* res, int nb) +{ + int rc; + + if (s == NULL || out == NULL || + (est_is_enroll(op) && (csr_der == NULL || csr_der_len == 0))) + return WOLFCERT_ERR_BAD_ARG; + + if (s->in_active && !nb) + return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "est", + "an async EST request is in flight on this session"); + + if (s->in_active) + rc = est_session_resume_check(s, op, out); + else if (op == EST_OP_SIMPLE_REENROLL && !s->has_client_identity) + rc = WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "est", + "reenroll needs a session opened with the cert being renewed"); + else + rc = est_session_begin(s, op, csr_der, csr_der_len, out); + if (rc != WOLFCERT_OK) return rc; - } - if (s->in_resp.status_code != 200) { - est_async_reset(s); - return WOLFCERT_ERR_HTTP; - } + if (nb) + rc = wolfcert_http_session_request_nb(s->http, &s->in_req, &s->in_resp); + else + rc = wolfcert_http_session_request(s->http, &s->in_req, &s->in_resp); - if (s->in_resp.body_len == 0) { - est_async_reset(s); - return WOLFCERT_ERR(WOLFCERT_ERR_HTTP, "est", - "200 response carried no certificate"); - } + if (nb && (rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE)) + return rc; - WolfCertBuffer p7 = { 0 }; - rc = wolfcert_base64_decode(s->in_resp.body, s->in_resp.body_len, &p7, s->heap); if (rc == WOLFCERT_OK) { - rc = wolfcert_pkcs7_certs_to_pem(p7.data, p7.len, out_ca_pem, s->heap); - wolfcert_buffer_free(&p7); + est_session_free_request(s); + if (res != NULL) + res->heap = s->heap; + rc = est_finish(s->heap, &s->in_resp, op, enc, out, res); } est_async_reset(s); return rc; } -/* Drive the async /simpleenroll into *out. `owner` is the caller's output - * pointer; a resumed call must pass the same one. */ -static int est_session_enroll_nb(WolfCertEstSession* s, const uint8_t* csr_der, - size_t csr_der_len, const void* owner, - WolfCertEstResult* out) +int wolfcert_est_session_get_cacerts(WolfCertEstSession* s, + WolfCertBuffer* out_ca_pem) { - int rc; + return est_session_call(s, EST_OP_GET_CACERTS, NULL, 0, + WOLFCERT_ENCODING_PEM, out_ca_pem, NULL, 0); +} - if (s == NULL || csr_der == NULL || csr_der_len == 0) - return WOLFCERT_ERR_BAD_ARG; +int wolfcert_est_session_get_cacerts_nb(WolfCertEstSession* s, + WolfCertBuffer* out_ca_pem) +{ + return est_session_call(s, EST_OP_GET_CACERTS, NULL, 0, + WOLFCERT_ENCODING_PEM, out_ca_pem, NULL, 1); +} - if (!s->in_active) { - s->in_url = join_path(s->base_url, "simpleenroll", s->heap); - if (s->in_url == NULL) - return WOLFCERT_ERR_MEMORY; +int wolfcert_est_session_get_cacerts_enc(WolfCertEstSession* s, + WolfCertEncoding enc, + WolfCertBuffer* out_ca) +{ + return est_session_call(s, EST_OP_GET_CACERTS, NULL, 0, enc, out_ca, + NULL, 0); +} - rc = wolfcert_base64_encode_mime(csr_der, csr_der_len, &s->in_body, s->heap); - if (rc != WOLFCERT_OK) { - WOLFCERT_XFREE(s->in_url, s->heap); - s->in_url = NULL; - return rc; - } +int wolfcert_est_session_get_cacerts_enc_nb(WolfCertEstSession* s, + WolfCertEncoding enc, + WolfCertBuffer* out_ca) +{ + return est_session_call(s, EST_OP_GET_CACERTS, NULL, 0, enc, out_ca, + NULL, 1); +} - s->in_req = (WolfCertHttpRequest){ - .method = "POST", - .url = s->in_url, - .content_type = "application/pkcs10", - .content_transfer_encoding = "base64", - .accept = "application/pkcs7-mime", - .basic_user = s->basic_user, - .basic_pass = s->basic_pass, - .body = s->in_body.data, - .body_len = s->in_body.len, - .max_response_bytes = s->max_body, - .heap = s->heap, - }; - s->in_owner = owner; - s->in_op = EST_OP_SIMPLE_ENROLL; - s->in_active = 1; - } - else { - rc = est_session_resume_check(s, EST_OP_SIMPLE_ENROLL, owner); - if (rc != WOLFCERT_OK) - return rc; - } +int wolfcert_est_session_get_csr_attrs(WolfCertEstSession* s, + WolfCertBuffer* out_attrs_der) +{ + return est_session_call(s, EST_OP_GET_CSRATTRS, NULL, 0, + WOLFCERT_ENCODING_DER, out_attrs_der, NULL, 0); +} - rc = wolfcert_http_session_request_nb(s->http, &s->in_req, &s->in_resp); - if (rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE) - return rc; +int wolfcert_est_session_get_csr_attrs_nb(WolfCertEstSession* s, + WolfCertBuffer* out_attrs_der) +{ + return est_session_call(s, EST_OP_GET_CSRATTRS, NULL, 0, + WOLFCERT_ENCODING_DER, out_attrs_der, NULL, 1); +} - if (rc == WOLFCERT_OK) { - out->heap = s->heap; - rc = est_enroll_finish(s->heap, &s->in_resp, out); - } +int wolfcert_est_session_simple_enroll_ex(WolfCertEstSession* s, + const uint8_t* csr_der, + size_t csr_der_len, + WolfCertEstResult* out) +{ + if (out == NULL) + return WOLFCERT_ERR_BAD_ARG; - est_async_reset(s); + memset(out, 0, sizeof(*out)); + return est_session_call(s, EST_OP_SIMPLE_ENROLL, csr_der, csr_der_len, + WOLFCERT_ENCODING_PEM, &out->cert_pem, out, 0); +} + +int wolfcert_est_session_simple_enroll(WolfCertEstSession* s, + const uint8_t* csr_der, size_t csr_der_len, + WolfCertBuffer* out_cert_pem) +{ + WolfCertEstResult r = { 0 }; + int rc = est_session_call(s, EST_OP_SIMPLE_ENROLL, csr_der, csr_der_len, + WOLFCERT_ENCODING_PEM, out_cert_pem, &r, 0); + + if (rc == WOLFCERT_OK && r.status == WOLFCERT_EST_STATUS_PENDING) + rc = WOLFCERT_ERR_PENDING; return rc; } @@ -658,8 +476,8 @@ int wolfcert_est_session_simple_enroll_nb_ex(WolfCertEstSession* s, return WOLFCERT_ERR_BAD_ARG; memset(out, 0, sizeof(*out)); - - return est_session_enroll_nb(s, csr_der, csr_der_len, out, out); + return est_session_call(s, EST_OP_SIMPLE_ENROLL, csr_der, csr_der_len, + WOLFCERT_ENCODING_PEM, &out->cert_pem, out, 1); } int wolfcert_est_session_simple_enroll_nb(WolfCertEstSession* s, @@ -668,185 +486,169 @@ int wolfcert_est_session_simple_enroll_nb(WolfCertEstSession* s, WolfCertBuffer* out_cert_pem) { WolfCertEstResult r = { 0 }; - int rc; + int rc = est_session_call(s, EST_OP_SIMPLE_ENROLL, csr_der, csr_der_len, + WOLFCERT_ENCODING_PEM, out_cert_pem, &r, 1); - if (s == NULL || csr_der == NULL || csr_der_len == 0 || out_cert_pem == NULL) - return WOLFCERT_ERR_BAD_ARG; - - rc = est_session_enroll_nb(s, csr_der, csr_der_len, out_cert_pem, &r); - return est_result_flatten(rc, &r, out_cert_pem); + if (rc == WOLFCERT_OK && r.status == WOLFCERT_EST_STATUS_PENDING) + rc = WOLFCERT_ERR_PENDING; + return rc; } -int wolfcert_est_session_get_cacerts(WolfCertEstSession* s, - WolfCertBuffer* out_ca_pem) +int wolfcert_est_session_simple_reenroll_ex(WolfCertEstSession* s, + const uint8_t* csr_der, + size_t csr_der_len, + WolfCertEstResult* out) { - if (s == NULL || out_ca_pem == NULL) + if (out == NULL) return WOLFCERT_ERR_BAD_ARG; - if (s->in_active) - return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "est", - "an async EST request is in flight on this session"); + memset(out, 0, sizeof(*out)); + return est_session_call(s, EST_OP_SIMPLE_REENROLL, csr_der, csr_der_len, + WOLFCERT_ENCODING_PEM, &out->cert_pem, out, 0); +} - char* url = join_path(s->base_url, "cacerts", s->heap); - if (url == NULL) - return WOLFCERT_ERR_MEMORY; +int wolfcert_est_session_simple_reenroll_nb_ex(WolfCertEstSession* s, + const uint8_t* csr_der, + size_t csr_der_len, + WolfCertEstResult* out) +{ + if (out == NULL) + return WOLFCERT_ERR_BAD_ARG; - WolfCertHttpRequest req = { .method = "GET", .url = url, - .accept = "application/pkcs7-mime", - .basic_user = s->basic_user, - .basic_pass = s->basic_pass, - .max_response_bytes = s->max_body, - .heap = s->heap }; - WolfCertHttpResponse resp = { 0 }; + memset(out, 0, sizeof(*out)); + return est_session_call(s, EST_OP_SIMPLE_REENROLL, csr_der, csr_der_len, + WOLFCERT_ENCODING_PEM, &out->cert_pem, out, 1); +} - int rc = wolfcert_http_session_request(s->http, &req, &resp); +static int est_one_shot(const WolfCertServerCfg* srv, int op, + const uint8_t* csr_der, size_t csr_der_len, + WolfCertEncoding enc, WolfCertBuffer* out, + WolfCertEstResult* res) +{ + WolfCertEstSession* s = NULL; + int rc; - WOLFCERT_XFREE(url, s->heap); - if (rc != WOLFCERT_OK) - return rc; + if (out == NULL || + (est_is_enroll(op) && (csr_der == NULL || csr_der_len == 0))) + return WOLFCERT_ERR_BAD_ARG; - if (resp.status_code != 200) { - wolfcert_http_response_free(&resp); - return WOLFCERT_ERR_HTTP; + rc = est_session_open_common(srv, &s); + if (rc == WOLFCERT_OK) { + wolfcert_http_session_set_close(s->http); + rc = est_session_call(s, op, csr_der, csr_der_len, enc, out, res, 0); } - if (resp.body_len == 0) { - wolfcert_http_response_free(&resp); - return WOLFCERT_ERR(WOLFCERT_ERR_HTTP, "est", - "200 response carried no certificate"); - } + wolfcert_est_session_close(s); + return rc; +} - WolfCertBuffer p7 = { 0 }; - rc = wolfcert_base64_decode(resp.body, resp.body_len, &p7, s->heap); +int wolfcert_est_get_cacerts(const WolfCertServerCfg* srv, WolfCertBuffer* out_ca_pem) +{ + return est_one_shot(srv, EST_OP_GET_CACERTS, NULL, 0, + WOLFCERT_ENCODING_PEM, out_ca_pem, NULL); +} - wolfcert_http_response_free(&resp); - if (rc != WOLFCERT_OK) - return rc; +int wolfcert_est_get_cacerts_enc(const WolfCertServerCfg* srv, WolfCertEncoding enc, + WolfCertBuffer* out_ca) +{ + return est_one_shot(srv, EST_OP_GET_CACERTS, NULL, 0, enc, out_ca, NULL); +} - rc = wolfcert_pkcs7_certs_to_pem(p7.data, p7.len, out_ca_pem, s->heap); +int wolfcert_est_get_csr_attrs(const WolfCertServerCfg* srv, + WolfCertBuffer* out_attrs_der) +{ + if (srv == NULL || out_attrs_der == NULL) + return WOLFCERT_ERR_BAD_ARG; - wolfcert_buffer_free(&p7); - return rc; + memset(out_attrs_der, 0, sizeof(*out_attrs_der)); + out_attrs_der->heap = srv->heap ? srv->heap : wolfcert_default_heap(); + return est_one_shot(srv, EST_OP_GET_CSRATTRS, NULL, 0, + WOLFCERT_ENCODING_DER, out_attrs_der, NULL); } -int wolfcert_est_session_simple_enroll_ex(WolfCertEstSession* s, - const uint8_t* csr_der, - size_t csr_der_len, - WolfCertEstResult* out) +int wolfcert_est_simple_enroll_ex(const WolfCertServerCfg* srv, + const uint8_t* csr_der, size_t csr_der_len, + WolfCertEstResult* out) { - int rc; - char* url = NULL; - WolfCertBuffer b64 = { 0 }; - WolfCertHttpRequest req; - WolfCertHttpResponse resp = { 0 }; - if (out == NULL) return WOLFCERT_ERR_BAD_ARG; memset(out, 0, sizeof(*out)); + return est_one_shot(srv, EST_OP_SIMPLE_ENROLL, csr_der, csr_der_len, + WOLFCERT_ENCODING_PEM, &out->cert_pem, out); +} - if (s == NULL || csr_der == NULL || csr_der_len == 0) - return WOLFCERT_ERR_BAD_ARG; - - if (s->in_active) - return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "est", - "an async EST request is in flight on this session"); - - url = join_path(s->base_url, "simpleenroll", s->heap); - if (url == NULL) - return WOLFCERT_ERR_MEMORY; - - rc = wolfcert_base64_encode_mime(csr_der, csr_der_len, &b64, s->heap); - if (rc != WOLFCERT_OK) { - WOLFCERT_XFREE(url, s->heap); - return rc; - } - - req = (WolfCertHttpRequest){ - .method = "POST", - .url = url, - .content_type = "application/pkcs10", - .content_transfer_encoding = "base64", - .accept = "application/pkcs7-mime", - .basic_user = s->basic_user, - .basic_pass = s->basic_pass, - .body = b64.data, - .body_len = b64.len, - .max_response_bytes = s->max_body, - .heap = s->heap, - }; - rc = wolfcert_http_session_request(s->http, &req, &resp); - - WOLFCERT_XFREE(url, s->heap); - wolfcert_buffer_free(&b64); - if (rc != WOLFCERT_OK) - return rc; +int wolfcert_est_simple_enroll(const WolfCertServerCfg* srv, + const uint8_t* csr_der, size_t csr_der_len, + WolfCertBuffer* out_cert_pem) +{ + WolfCertEstResult r = { 0 }; + int rc = est_one_shot(srv, EST_OP_SIMPLE_ENROLL, csr_der, csr_der_len, + WOLFCERT_ENCODING_PEM, out_cert_pem, &r); - out->heap = s->heap; - return est_enroll_finish(s->heap, &resp, out); + if (rc == WOLFCERT_OK && r.status == WOLFCERT_EST_STATUS_PENDING) + rc = WOLFCERT_ERR_PENDING; + return rc; } -int wolfcert_est_session_simple_enroll(WolfCertEstSession* s, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertBuffer* out_cert_pem) +int wolfcert_est_simple_reenroll_ex(const WolfCertServerCfg* srv, + const uint8_t* current_cert, size_t current_cert_len, + const WolfCertKey* current_key, + const uint8_t* csr_der, size_t csr_der_len, + WolfCertEstResult* out) { - WolfCertEstResult r = { 0 }; + WolfCertServerCfg cfg; + WolfCertBuffer key_pem = { 0 }; int rc; - if (s == NULL || csr_der == NULL || csr_der_len == 0 || out_cert_pem == NULL) + if (out == NULL) return WOLFCERT_ERR_BAD_ARG; - rc = wolfcert_est_session_simple_enroll_ex(s, csr_der, csr_der_len, &r); - return est_result_flatten(rc, &r, out_cert_pem); -} + memset(out, 0, sizeof(*out)); -int wolfcert_est_get_csr_attrs(const WolfCertServerCfg* srv, - WolfCertBuffer* out_attrs_der) -{ - if (srv == NULL || srv->server_url == NULL || out_attrs_der == NULL) + if (srv == NULL || current_cert == NULL || current_key == NULL) return WOLFCERT_ERR_BAD_ARG; - void* heap = srv->heap ? srv->heap : wolfcert_default_heap(); - memset(out_attrs_der, 0, sizeof(*out_attrs_der)); - out_attrs_der->heap = heap; - int trc = est_check_cfg(srv, heap); - if (trc != WOLFCERT_OK) - return trc; - - char* url = join_path(srv->server_url, "csrattrs", heap); - if (url == NULL) - return WOLFCERT_ERR_MEMORY; + /* The TLS identity is the cert being renewed. */ + rc = wolfcert_key_to_pem(current_key, &key_pem); + if (rc == WOLFCERT_OK) { + cfg = *srv; + cfg.client_cert = current_cert; + cfg.client_cert_len = current_cert_len; + cfg.client_key = key_pem.data; + cfg.client_key_len = key_pem.len; + rc = est_one_shot(&cfg, EST_OP_SIMPLE_REENROLL, csr_der, csr_der_len, + WOLFCERT_ENCODING_PEM, &out->cert_pem, out); + } - WolfCertHttpRequest req = { .method = "GET", .url = url, - .accept = "application/csrattrs" }; - fill_common(srv, &req); + if (key_pem.data != NULL) + wc_ForceZero(key_pem.data, (word32)key_pem.len); + wolfcert_buffer_free(&key_pem); + return rc; +} - WolfCertHttpResponse resp = { 0 }; - int rc = wolfcert_http_request(&req, &resp); +int wolfcert_est_simple_reenroll(const WolfCertServerCfg* srv, + const uint8_t* current_cert, size_t current_cert_len, + const WolfCertKey* current_key, + const uint8_t* csr_der, size_t csr_der_len, + WolfCertBuffer* out_cert_pem) +{ + WolfCertEstResult r; + int rc; - WOLFCERT_XFREE(url, heap); - if (rc != WOLFCERT_OK) - return rc; + if (out_cert_pem == NULL) + return WOLFCERT_ERR_BAD_ARG; - /* RFC 7030 section 4.5.2: 204 and 404 both mean no CSR attributes. */ - if (resp.status_code == 204 || resp.status_code == 404 || - resp.body_len == 0) { - wolfcert_http_response_free(&resp); - return WOLFCERT_OK; + rc = wolfcert_est_simple_reenroll_ex(srv, current_cert, current_cert_len, + current_key, csr_der, csr_der_len, &r); + if (rc == WOLFCERT_OK && r.status == WOLFCERT_EST_STATUS_PENDING) { + rc = WOLFCERT_ERR_PENDING; } - - if (resp.status_code != 200) { - wolfcert_http_response_free(&resp); - return WOLFCERT_ERR_HTTP; + else if (rc == WOLFCERT_OK) { + *out_cert_pem = r.cert_pem; + memset(&r.cert_pem, 0, sizeof(r.cert_pem)); } - WolfCertBuffer dec = { 0 }; - rc = wolfcert_base64_decode(resp.body, resp.body_len, &dec, heap); - - wolfcert_http_response_free(&resp); - if (rc != WOLFCERT_OK) - return rc; - - *out_attrs_der = dec; - return WOLFCERT_OK; + wolfcert_est_result_free(&r); + return rc; } diff --git a/src/http.c b/src/http.c index 2d8286c..6519cf3 100644 --- a/src/http.c +++ b/src/http.c @@ -1122,6 +1122,12 @@ int wolfcert_http_session_open(const WolfCertHttpSessionCfg* cfg, return WOLFCERT_OK; } +void wolfcert_http_session_set_close(WolfCertHttpSession* s) +{ + if (s != NULL) + s->keep_alive = 0; +} + int wolfcert_http_session_fd(const WolfCertHttpSession* s) { #ifdef WOLFCERT_HAVE_BUILTIN_TRANSPORT @@ -1909,7 +1915,7 @@ int wolfcert_http_request(const WolfCertHttpRequest* req, WolfCertHttpResponse* rc = wolfcert_http_session_open(&cfg, &s); if (rc == WOLFCERT_OK) { - s->keep_alive = 0; + wolfcert_http_session_set_close(s); rc = http_session_call(s, req, resp, 0); } diff --git a/src/internal.h b/src/internal.h index 9949995..a6386b9 100644 --- a/src/internal.h +++ b/src/internal.h @@ -26,6 +26,7 @@ #define WOLFCERT_INTERNAL_H #include +#include #include #include #include @@ -304,6 +305,8 @@ int wolfcert_http_hdr_is(const char* line, size_t llen, const char* name); * value after leading whitespace, or *val NULL when the line has no colon. */ int wolfcert_http_hdr_split(const char* line, size_t llen, const char** val, size_t* vlen); +/* Send Connection: close on the session's requests. */ +void wolfcert_http_session_set_close(WolfCertHttpSession* s); /* `_encode` emits one line; `_encode_mime` wraps at 64 columns, which libest's * BIO_f_base64 body parser requires. `_decode` accepts both. */ diff --git a/tests/integration/test_est_csr_attrs_roundtrip.c b/tests/integration/test_est_csr_attrs_roundtrip.c index 7d20a03..53326f0 100644 --- a/tests/integration/test_est_csr_attrs_roundtrip.c +++ b/tests/integration/test_est_csr_attrs_roundtrip.c @@ -30,6 +30,7 @@ #include "tls_test_util.h" +#include #include #include #include @@ -49,6 +50,14 @@ static void* server_thread(void* arg) return NULL; } +/* Wait on the session fd in the direction a WANT_* return asked for. */ +static int wait_io(WolfCertEstSession* s, int rc) +{ + struct pollfd p = { .fd = wolfcert_est_session_fd(s), + .events = (rc == WOLFCERT_ERR_WANT_WRITE) ? POLLOUT : POLLIN }; + return poll(&p, 1, 5000) > 0 ? 0 : -1; +} + static const uint8_t OID_CHALLENGE_PASSWORD[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x07 }; @@ -118,6 +127,24 @@ int main(void) REQUIRE(raw.len == blob.len); REQUIRE(memcmp(raw.data, blob.data, blob.len) == 0); + WolfCertEstSession* sess = NULL; + WolfCertBuffer sraw = { 0 }; + int rc; + REQUIRE(wolfcert_est_session_open(&cli, &sess) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_get_csr_attrs(sess, &sraw) == WOLFCERT_OK); + wolfcert_est_session_close(sess); + REQUIRE(sraw.len == blob.len && memcmp(sraw.data, blob.data, blob.len) == 0); + wolfcert_buffer_free(&sraw); + + REQUIRE(wolfcert_est_session_open_async(&cli, &sess) == WOLFCERT_OK); + while ((rc = wolfcert_est_session_get_csr_attrs_nb(sess, &sraw)) + == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE) + REQUIRE(wait_io(sess, rc) == 0); + wolfcert_est_session_close(sess); + REQUIRE(rc == WOLFCERT_OK); + REQUIRE(sraw.len == blob.len && memcmp(sraw.data, blob.data, blob.len) == 0); + wolfcert_buffer_free(&sraw); + WolfCertCsrAttrs parsed; REQUIRE(wolfcert_est_parse_csr_attrs(raw.data, raw.len, &parsed) == WOLFCERT_OK); REQUIRE(parsed.count == 3); diff --git a/tests/integration/test_est_mtls_roundtrip.c b/tests/integration/test_est_mtls_roundtrip.c index 1b8fdbf..b2580ed 100644 --- a/tests/integration/test_est_mtls_roundtrip.c +++ b/tests/integration/test_est_mtls_roundtrip.c @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -52,6 +53,14 @@ static void* server_thread(void* arg) { wolfcert_server_run((WolfCertServer*)arg); return NULL; } #ifdef KEEP_PEER_CERT +/* Wait on the session fd in the direction a WANT_* return asked for. */ +static int wait_io(WolfCertEstSession* s, int rc) +{ + struct pollfd p = { .fd = wolfcert_est_session_fd(s), + .events = (rc == WOLFCERT_ERR_WANT_WRITE) ? POLLOUT : POLLIN }; + return poll(&p, 1, 5000) > 0 ? 0 : -1; +} + /* Re-enroll the trusted cli_cert while cfg carries an identity the server * does not trust; the handshake must present the cert being renewed. */ static int test_reenroll_ignores_cfg_identity(const char* url, @@ -68,6 +77,8 @@ static int test_reenroll_ignores_cfg_identity(const char* url, WolfCertBuffer csr = { 0 }; WolfCertBuffer issued = { 0 }; WolfCertBuffer ca_pem = { 0 }; + WolfCertEstSession* sess = NULL; + WolfCertEstResult res = { 0 }; WolfCertKeyCfg kcfg = { .type = TEST_ENROLL_KEY_TYPE, .param = TEST_ENROLL_KEY_PARAM, .dev_id = WOLFCERT_DEVID_SOFTWARE }; WolfCertCertMeta meta = { .subject_dn = "CN=factory-bootstrap" }; @@ -103,6 +114,26 @@ static int test_reenroll_ignores_cfg_identity(const char* url, REQUIRE(rc == WOLFCERT_OK); REQUIRE(memmem(issued.data, issued.len, "BEGIN CERTIFICATE", 17) != NULL); + /* A session reenrolls with the identity it was opened with. */ + cli.client_cert = cli_cert; + cli.client_cert_len = cli_cert_len; + cli.client_key = cli_key; + cli.client_key_len = cli_key_len; + REQUIRE(wolfcert_est_session_open(&cli, &sess) == WOLFCERT_OK); + rc = wolfcert_est_session_simple_reenroll_ex(sess, csr.data, csr.len, &res); + wolfcert_est_session_close(sess); + REQUIRE(rc == WOLFCERT_OK && res.status == WOLFCERT_EST_STATUS_SUCCESS); + wolfcert_est_result_free(&res); + + REQUIRE(wolfcert_est_session_open_async(&cli, &sess) == WOLFCERT_OK); + while ((rc = wolfcert_est_session_simple_reenroll_nb_ex(sess, csr.data, + csr.len, &res)) + == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE) + REQUIRE(wait_io(sess, rc) == 0); + wolfcert_est_session_close(sess); + REQUIRE(rc == WOLFCERT_OK && res.status == WOLFCERT_EST_STATUS_SUCCESS); + wolfcert_est_result_free(&res); + wolfcert_buffer_free(&issued); wolfcert_buffer_free(&csr); wolfcert_key_free(dk); @@ -549,6 +580,16 @@ int main(void) REQUIRE(rc != WOLFCERT_OK); if (rc == WOLFCERT_OK) wolfcert_buffer_free(&ca_pem); + + /* A session without a client cert refuses reenroll before sending. */ + static const uint8_t csr[] = { 0x30, 0x03, 0x02, 0x01, 0x00 }; + WolfCertEstSession* ns = NULL; + WolfCertEstResult nr = { 0 }; + REQUIRE(wolfcert_est_session_open(&cli, &ns) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_simple_reenroll_ex(ns, csr, sizeof(csr), + &nr) + == WOLFCERT_ERR_BAD_ARG); + wolfcert_est_session_close(ns); } { diff --git a/tests/integration/test_est_pha_roundtrip.c b/tests/integration/test_est_pha_roundtrip.c index 5c4b2e7..b431d83 100644 --- a/tests/integration/test_est_pha_roundtrip.c +++ b/tests/integration/test_est_pha_roundtrip.c @@ -17,8 +17,7 @@ * along with wolfCert. If not, see . */ -/* TLS 1.3 post-handshake auth over the keep-alive EST session. Reenroll over - * PHA uses a raw TLS client, since sessions have no reenroll call. */ +/* TLS 1.3 post-handshake auth over the keep-alive EST session. */ #define _POSIX_C_SOURCE 200809L #define _DEFAULT_SOURCE @@ -30,7 +29,6 @@ #include #include -#include #include #include "tls_test_util.h" @@ -53,58 +51,6 @@ static void* server_thread(void* arg) { wolfcert_server_run((WolfCertServer*)arg); return NULL; } -#ifdef WOLFSSL_POST_HANDSHAKE_AUTH -/* POST a renewal CSR for cli_cert over a PHA connection and expect a 200. */ -static int pha_reenroll(uint16_t port, const uint8_t* tls_cert, size_t tls_cert_len, - const uint8_t* cli_cert, size_t cli_cert_len, - const uint8_t* cli_key, size_t cli_key_len) -{ - WolfCertKeyCfg kcfg = { .type = TEST_ENROLL_KEY_TYPE, .param = TEST_ENROLL_KEY_PARAM, - .dev_id = WOLFCERT_DEVID_SOFTWARE }; - WolfCertCertMeta meta = { .subject_dn = "CN=factory-bootstrap" }; - WolfCertKey* dk = NULL; - WolfCertBuffer csr = { 0 }; - TestTlsConn c; - char req[4096]; - byte b64[3072]; - word32 b64_len = sizeof(b64); - char resp[64] = { 0 }; - int n; - - REQUIRE(wolfcert_key_generate(&kcfg, &dk) == WOLFCERT_OK); - REQUIRE(wolfcert_csr_build(dk, &meta, &csr) == WOLFCERT_OK); - wolfcert_key_free(dk); - REQUIRE(Base64_Encode_NoNl(csr.data, (word32)csr.len, b64, &b64_len) == 0); - wolfcert_buffer_free(&csr); - n = snprintf(req, sizeof(req), - "POST /.well-known/est/simplereenroll HTTP/1.1\r\n" - "Host: 127.0.0.1\r\nContent-Type: application/pkcs10\r\n" - "Content-Length: %u\r\nConnection: close\r\n\r\n%.*s", - (unsigned)b64_len, (int)b64_len, (const char*)b64); - REQUIRE(n > 0 && (size_t)n < sizeof(req)); - - /* wolfSSL unloads an SSL-level identity after the handshake, so it goes - * on the CTX. */ - REQUIRE(test_tls_setup(&c, port, tls_cert, tls_cert_len) == 0); - REQUIRE(wolfSSL_CTX_use_certificate_buffer(c.ctx, cli_cert, (long)cli_cert_len, - WOLFSSL_FILETYPE_PEM) == WOLFSSL_SUCCESS); - REQUIRE(wolfSSL_CTX_use_PrivateKey_buffer(c.ctx, cli_key, (long)cli_key_len, - WOLFSSL_FILETYPE_PEM) == WOLFSSL_SUCCESS); - wolfSSL_free(c.ssl); - c.ssl = wolfSSL_new(c.ctx); - REQUIRE(c.ssl != NULL && wolfSSL_set_fd(c.ssl, c.fd) == WOLFSSL_SUCCESS); - REQUIRE(wolfSSL_allow_post_handshake_auth(c.ssl) == 0); - REQUIRE(wolfSSL_connect(c.ssl) == WOLFSSL_SUCCESS); - REQUIRE(test_tls_write(&c, req, (size_t)n) == 0); - REQUIRE(test_tls_read(&c, resp, sizeof(resp) - 1) > 0); - test_tls_close(&c); - if (strncmp(resp, "HTTP/1.1 200", 12) != 0) - fprintf(stderr, "pha reenroll: %s\n", resp); - REQUIRE(strncmp(resp, "HTTP/1.1 200", 12) == 0); - return 0; -} -#endif - #ifdef WOLFSSL_POST_HANDSHAKE_AUTH static int discard_send(WOLFSSL* ssl, char* buf, int sz, void* ctx) { @@ -291,10 +237,44 @@ int main(void) REQUIRE(expect_enroll_refused(&cli, "CN=pha-no-opt-in") == 0); } + /* Reenroll over PHA with a CSR for cli_cert's own subject. */ + { + WolfCertServerCfg cli = { + .protocol = WOLFCERT_PROTO_EST, + .server_url = url, + .trust_anchors = tls_cert, + .trust_anchors_len = tls_cert_len, + .verify_server = 1, + .client_cert = cli_cert, + .client_cert_len = cli_cert_len, + .client_key = cli_key, + .client_key_len = cli_key_len, + .proto_opts.est = { .allow_post_handshake_auth = 1 }, + }; + WolfCertKeyCfg kcfg = { .type = TEST_ENROLL_KEY_TYPE, .param = TEST_ENROLL_KEY_PARAM, + .dev_id = WOLFCERT_DEVID_SOFTWARE }; + WolfCertCertMeta meta = { .subject_dn = "CN=factory-bootstrap" }; + WolfCertKey* dk = NULL; + WolfCertBuffer csr = { 0 }; + WolfCertEstSession* s = NULL; + WolfCertEstResult issued = { 0 }; + + REQUIRE(wolfcert_key_generate(&kcfg, &dk) == WOLFCERT_OK); + REQUIRE(wolfcert_csr_build(dk, &meta, &csr) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_open(&cli, &s) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_simple_reenroll_ex(s, csr.data, csr.len, + &issued) == WOLFCERT_OK); + REQUIRE(issued.status == WOLFCERT_EST_STATUS_SUCCESS); + REQUIRE(memmem(issued.cert_pem.data, issued.cert_pem.len, + "BEGIN CERTIFICATE", 17) != NULL); + + wolfcert_est_result_free(&issued); + wolfcert_est_session_close(s); + wolfcert_buffer_free(&csr); + wolfcert_key_free(dk); + } + #ifdef WOLFSSL_POST_HANDSHAKE_AUTH - /* Reenroll over PHA with a CSR for cli_cert's own subject gets 200. */ - REQUIRE(pha_reenroll(wolfcert_server_port(srv), tls_cert, tls_cert_len, - cli_cert, cli_cert_len, cli_key, cli_key_len) == 0); /* A client that never answers the CertificateRequest gets a 403. */ { diff --git a/tests/integration/test_est_roundtrip.c b/tests/integration/test_est_roundtrip.c index 2efc58c..4339c53 100644 --- a/tests/integration/test_est_roundtrip.c +++ b/tests/integration/test_est_roundtrip.c @@ -283,6 +283,17 @@ int main(void) REQUIRE(wolfSSL_CertManagerLoadCABuffer(ca_cm, ca_der.data, (long)ca_der.len, WOLFSSL_FILETYPE_ASN1) == WOLFSSL_SUCCESS); wolfSSL_CertManagerFree(ca_cm); + + WolfCertEstSession* ca_sess = NULL; + WolfCertBuffer sess_der = { 0 }; + REQUIRE(wolfcert_est_session_open(&client_cfg, &ca_sess) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_get_cacerts_enc(ca_sess, WOLFCERT_ENCODING_DER, + &sess_der) == WOLFCERT_OK); + wolfcert_est_session_close(ca_sess); + REQUIRE(sess_der.len == ca_der.len && + memcmp(sess_der.data, ca_der.data, ca_der.len) == 0); + wolfcert_buffer_free(&sess_der); + wolfcert_buffer_free(&ca_der); wolfcert_client_free(client); diff --git a/tests/unit/test_est.c b/tests/unit/test_est.c index bed2567..8a421e9 100644 --- a/tests/unit/test_est.c +++ b/tests/unit/test_est.c @@ -96,7 +96,7 @@ static int make_test_ca(uint8_t* out, size_t cap, size_t* out_len) struct srv_ctx { int listen_fd; uint8_t* body; size_t len; WOLFSSL_CTX* ctx; char request[8192]; size_t request_len; - int post_missing_ctenc; }; + int post_missing_ctenc; int status; int conns; }; /* Returns a bound loopback listener and its ephemeral port. */ static int listen_loopback(int* port) @@ -199,11 +199,12 @@ static void handle_conn(int cs, struct srv_ctx* sc) char hdr[256]; int hn = snprintf(hdr, sizeof(hdr), - "HTTP/1.1 200 OK\r\n" + "HTTP/1.1 %d X\r\n" "Content-Type: application/pkcs7-mime; smime-type=certs-only\r\n" "Content-Transfer-Encoding: base64\r\n" "Content-Length: %zu\r\n" - "Connection: close\r\n\r\n", sc->len); + "Connection: close\r\n\r\n", sc->status ? sc->status : 200, + sc->len); tls_write_all(ssl, hdr, hn); tls_write_all(ssl, sc->body, (int)sc->len); wolfSSL_shutdown(ssl); @@ -215,7 +216,7 @@ static void* srv_thread(void* arg) { struct srv_ctx* sc = (struct srv_ctx*)arg; - for (int i = 0; i < 5; ++i) { + for (int i = 0; i < (sc->conns ? sc->conns : 5); ++i) { int cs = accept(sc->listen_fd, NULL, NULL); if (cs < 0) break; @@ -319,6 +320,11 @@ static int test_est_require_server_auth(void) REQUIRE(wolfcert_est_get_cacerts(&srv, &out) == WOLFCERT_ERR_IO); REQUIRE(wolfcert_est_session_open(&srv, &sess) == WOLFCERT_ERR_IO); + /* Bad arguments fail before the dial. */ + REQUIRE(wolfcert_est_get_cacerts(&srv, NULL) == WOLFCERT_ERR_BAD_ARG); + REQUIRE(wolfcert_est_simple_enroll(&srv, NULL, 0, &out) + == WOLFCERT_ERR_BAD_ARG); + return 0; } @@ -535,6 +541,8 @@ static int check_empty_body(WOLFSSL_CTX* ctx, const WolfCertServerCfg* tmpl, REQUIRE(wolfcert_est_get_cacerts(&srv, &out) == WOLFCERT_ERR_HTTP); REQUIRE(out.data == NULL); + /* A one-shot closes after its request; a session keeps the connection. */ + REQUIRE(strstr(sc.request, "Connection: close\r\n") != NULL); REQUIRE(wolfcert_est_simple_enroll(&srv, csr, csr_len, &out) == WOLFCERT_ERR_HTTP); REQUIRE(out.data == NULL); @@ -546,6 +554,7 @@ static int check_empty_body(WOLFSSL_CTX* ctx, const WolfCertServerCfg* tmpl, REQUIRE(wolfcert_est_session_get_cacerts(sess, &out) == WOLFCERT_ERR_HTTP); wolfcert_est_session_close(sess); REQUIRE(out.data == NULL); + REQUIRE(strstr(sc.request, "Connection: keep-alive\r\n") != NULL); sess = NULL; REQUIRE(wolfcert_est_session_open(&srv, &sess) == WOLFCERT_OK); @@ -558,6 +567,43 @@ static int check_empty_body(WOLFSSL_CTX* ctx, const WolfCertServerCfg* tmpl, return 0; } +/* 401 is WOLFCERT_ERR_AUTH from every EST call, and an empty 500 does not + * mean "no CSR attributes". */ +static int check_error_status(WOLFSSL_CTX* ctx, const WolfCertServerCfg* tmpl) +{ + struct srv_ctx sc = { .ctx = ctx, .status = 401, .conns = 3 }; + WolfCertServerCfg srv = *tmpl; + WolfCertEstSession* sess = NULL; + WolfCertBuffer out = { 0 }; + pthread_t tid; + char url[128]; + int port = 0; + + sc.listen_fd = listen_loopback(&port); + REQUIRE(sc.listen_fd >= 0); + REQUIRE(pthread_create(&tid, NULL, srv_thread, &sc) == 0); + snprintf(url, sizeof(url), "https://127.0.0.1:%d/.well-known/est", port); + srv.server_url = url; + + REQUIRE(wolfcert_est_get_cacerts(&srv, &out) == WOLFCERT_ERR_AUTH); + REQUIRE(wolfcert_est_get_csr_attrs(&srv, &out) == WOLFCERT_ERR_AUTH); + REQUIRE(wolfcert_est_session_open(&srv, &sess) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_get_cacerts(sess, &out) == WOLFCERT_ERR_AUTH); + wolfcert_est_session_close(sess); + pthread_join(tid, NULL); + + sc = (struct srv_ctx){ .ctx = ctx, .status = 500, .conns = 1 }; + sc.listen_fd = listen_loopback(&port); + REQUIRE(sc.listen_fd >= 0); + REQUIRE(pthread_create(&tid, NULL, srv_thread, &sc) == 0); + snprintf(url, sizeof(url), "https://127.0.0.1:%d/.well-known/est", port); + + REQUIRE(wolfcert_est_get_csr_attrs(&srv, &out) == WOLFCERT_ERR_HTTP); + REQUIRE(out.data == NULL); + pthread_join(tid, NULL); + return 0; +} + int main(void) { /* The mock responder may write after the client closed the connection. */ @@ -670,6 +716,7 @@ int main(void) REQUIRE(check_empty_body(ctx, &srv, csr.data, csr.len, ca_der, ca_len, dk) == 0); + REQUIRE(check_error_status(ctx, &srv) == 0); wolfcert_buffer_free(&csr); wolfcert_key_free(dk); diff --git a/wolfcert/est.h b/wolfcert/est.h index 965e761..89f618c 100644 --- a/wolfcert/est.h +++ b/wolfcert/est.h @@ -27,12 +27,14 @@ extern "C" { #endif -/* GET /.well-known/est/cacerts - returns the CA chain as PEM. */ +/* GET /.well-known/est/cacerts - returns the CA chain as PEM. + * A 401 or 403 returns WOLFCERT_ERR_AUTH, any other non-200 _HTTP. */ WOLFCERT_API int wolfcert_est_get_cacerts(const WolfCertServerCfg* srv, WolfCertBuffer* out_ca_pem); /* GET /.well-known/est/csrattrs - returns the CsrAttrs DER for - * wolfcert_est_parse_csr_attrs, empty (NULL, 0) on 204, 404 or no body. */ + * wolfcert_est_parse_csr_attrs, empty (NULL, 0) on 204, 404, 405, 501 or a + * 200 with no body. A 401 or 403 returns WOLFCERT_ERR_AUTH, others _HTTP. */ WOLFCERT_API int wolfcert_est_get_csr_attrs(const WolfCertServerCfg* srv, WolfCertBuffer* out_attrs_der); @@ -172,6 +174,13 @@ WOLFCERT_API int wolfcert_est_session_open(const WolfCertServerCfg* srv, WOLFCERT_API int wolfcert_est_session_get_cacerts(WolfCertEstSession* s, WolfCertBuffer* out_ca_pem); +WOLFCERT_API int wolfcert_est_session_get_cacerts_enc(WolfCertEstSession* s, + WolfCertEncoding enc, + WolfCertBuffer* out_ca); + +WOLFCERT_API int wolfcert_est_session_get_csr_attrs(WolfCertEstSession* s, + WolfCertBuffer* out_attrs_der); + WOLFCERT_API int wolfcert_est_session_simple_enroll(WolfCertEstSession* s, const uint8_t* csr_der, size_t csr_der_len, WolfCertBuffer* out_cert_pem); @@ -181,6 +190,13 @@ WOLFCERT_API int wolfcert_est_session_simple_enroll_ex(WolfCertEstSession* s, size_t csr_der_len, WolfCertEstResult* out); +/* The session's TLS client credential, srv->client_cert and client_key at + * open, is the cert being renewed; WOLFCERT_ERR_BAD_ARG without one. */ +WOLFCERT_API int wolfcert_est_session_simple_reenroll_ex(WolfCertEstSession* s, + const uint8_t* csr_der, + size_t csr_der_len, + WolfCertEstResult* out); + WOLFCERT_API void wolfcert_est_session_close(WolfCertEstSession* s); /* Socket fd of the backing HTTP session - hand to poll/epoll/kqueue; @@ -199,6 +215,13 @@ WOLFCERT_API int wolfcert_est_session_open_async(const WolfCertServerCfg* srv, WOLFCERT_API int wolfcert_est_session_get_cacerts_nb(WolfCertEstSession* s, WolfCertBuffer* out_ca_pem); +WOLFCERT_API int wolfcert_est_session_get_cacerts_enc_nb(WolfCertEstSession* s, + WolfCertEncoding enc, + WolfCertBuffer* out_ca); + +WOLFCERT_API int wolfcert_est_session_get_csr_attrs_nb(WolfCertEstSession* s, + WolfCertBuffer* out_attrs_der); + WOLFCERT_API int wolfcert_est_session_simple_enroll_nb(WolfCertEstSession* s, const uint8_t* csr_der, size_t csr_der_len, @@ -209,6 +232,11 @@ WOLFCERT_API int wolfcert_est_session_simple_enroll_nb_ex(WolfCertEstSession* s, size_t csr_der_len, WolfCertEstResult* out); +WOLFCERT_API int wolfcert_est_session_simple_reenroll_nb_ex(WolfCertEstSession* s, + const uint8_t* csr_der, + size_t csr_der_len, + WolfCertEstResult* out); + #ifdef __cplusplus } #endif diff --git a/wolfcert/http.h b/wolfcert/http.h index 001cfc5..619068a 100644 --- a/wolfcert/http.h +++ b/wolfcert/http.h @@ -87,7 +87,8 @@ WOLFCERT_API void wolfcert_http_response_free(WolfCertHttpResponse* resp); typedef struct WolfCertHttpSession WolfCertHttpSession; typedef struct { - const char* base_url; /* e.g. https://ca.example */ + /* Server to connect to, e.g. https://ca.example; a path is ignored. */ + const char* base_url; const uint8_t* trust_anchors; size_t trust_anchors_len; int verify_server; diff --git a/wolfcert/types.h b/wolfcert/types.h index d103bce..9cad4e4 100644 --- a/wolfcert/types.h +++ b/wolfcert/types.h @@ -166,13 +166,13 @@ typedef struct { const char* username; const char* password; - /* TLS 1.3 post-handshake auth opt-in (RFC 8446 section 4.6.2), read only - * by the keep-alive EST session API. */ + /* TLS 1.3 post-handshake auth opt-in (RFC 8446 section 4.6.2) for every + * EST call; WOLFCERT_ERR_UNSUPPORTED if wolfSSL lacks it. */ int allow_post_handshake_auth; /* When set, wolfcert_client_enroll fetches /csrattrs and applies its * hints to copies of key_cfg and meta, filling only zero-valued fields. - * An empty reply (204, 404 or no body) changes nothing. */ + * An empty /csrattrs reply changes nothing. */ int auto_csrattrs; } WolfCertEstServerOpts; @@ -211,8 +211,8 @@ typedef struct { int verify_server; /* must be 1 for EST and https:// SCEP */ int timeout_ms; /* per-request timeout; 0 = default */ - /* Optional mutual-TLS client identity (both set), PEM or DER. EST - * /simplereenroll presents the cert being renewed instead. */ + /* Optional mutual-TLS client identity (both set), PEM or DER. For EST, a + * session's identity is the cert its /simplereenroll renews. */ const uint8_t* client_cert; size_t client_cert_len; const uint8_t* client_key; From e045daba05072f30c21520696b850185f00f8598 Mon Sep 17 00:00:00 2001 From: Yosuke Shimizu Date: Mon, 19 Oct 2026 16:45:50 +0900 Subject: [PATCH 2/5] Drop the buffer-returning enroll calls; take their names for the _ex calls - The EST and SCEP enroll calls returning a WolfCertBuffer are removed; the result-returning calls take the plain names, and EST's _nb_ex calls become _nb. - wolfcert_client_enroll() and wolfcert_client_reenroll() map a PENDING result to WOLFCERT_ERR_PENDING, as client.h now states. - The CLI, examples, headers, docs and tests use the result form. - test_est_mtls_roundtrip: a matching renewal under approval is PENDING, then issued on its re-POST, and wolfcert_client_reenroll() returns WOLFCERT_ERR_PENDING. - errors.h and wolfcert_strerror() describe WOLFCERT_ERR_PENDING as a pending EST enrollment from the client calls. --- AGENTS.md | 10 +- cli/wolfcert_client.c | 47 ++--- docs/ARCHITECTURE.md | 18 +- docs/MIGRATING-FROM-WOLFSCEP.md | 12 +- examples/enroll_cryptocb.c | 13 +- examples/enroll_est.c | 13 +- examples/enroll_scep.c | 18 +- src/client.c | 27 ++- src/errors.c | 2 +- src/est/est_client.c | 114 +++-------- src/scep/scep_client.c | 164 +++++---------- tests/integration/est_client_cases.h | 40 ++-- tests/integration/test_est_async_roundtrip.c | 49 ++--- tests/integration/test_est_mldsa_roundtrip.c | 12 +- tests/integration/test_est_mtls_roundtrip.c | 120 ++++++----- .../integration/test_est_pending_roundtrip.c | 188 +++++------------- tests/integration/test_est_pha_roundtrip.c | 22 +- tests/integration/test_est_roundtrip.c | 30 +-- tests/integration/test_est_tls_roundtrip.c | 9 +- tests/integration/test_scep_async_roundtrip.c | 62 +++--- tests/integration/test_scep_get_cert.c | 34 ++-- tests/integration/test_scep_poll_roundtrip.c | 56 +++--- tests/integration/test_scep_roundtrip.c | 172 +++++++++------- tests/unit/test_est.c | 81 ++++---- tests/unit/test_scep_msg.c | 92 ++++----- wolfcert/client.h | 7 +- wolfcert/errors.h | 6 +- wolfcert/est.h | 53 ++--- wolfcert/scep.h | 84 +++----- 29 files changed, 687 insertions(+), 868 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f872270..fe0ace6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -170,10 +170,10 @@ wolfcert_cleanup(); `examples/enroll_scep.c` is the full flow. - **Step by step:** `wolfcert_key_generate`, `wolfcert_csr_build`, then `wolfcert_est_simple_enroll` (`examples/enroll_est.c`). -- **Pending approval:** the `_ex` variants report a status of PENDING; poll - again later (EST: re-POST after `retry_after_sec`; SCEP: - `wolfcert_scep_get_cert_initial`). The plain variants return - `WOLFCERT_ERR_PENDING`. +- **Pending approval:** the enroll calls report PENDING in their result's + status; poll again later (EST: re-POST after `retry_after_sec`; SCEP: + `wolfcert_scep_get_cert_initial`). `wolfcert_client_enroll` and + `wolfcert_client_reenroll` return `WOLFCERT_ERR_PENDING`. - **Renewal:** `wolfcert_client_reenroll` (EST, authenticates with the certificate being renewed) or `wolfcert_scep_renewal_req`. - **Hardware keys:** `examples/enroll_cryptocb.c`. @@ -184,7 +184,7 @@ wolfcert_cleanup(); |---|---| | `wolfcert/wolfcert.h` | Umbrella header, `wolfcert_init` / `wolfcert_cleanup` | | `wolfcert/client.h` | High-level `wolfcert_client_*` API | -| `wolfcert/est.h`, `wolfcert/scep.h` | Protocol one-shots, `_ex` PENDING variants, sessions, non-blocking `_nb` sessions | +| `wolfcert/est.h`, `wolfcert/scep.h` | Protocol one-shots, sessions, non-blocking `_nb` sessions | | `wolfcert/types.h` | `WolfCertServerCfg`, `WolfCertKeyCfg`, `WolfCertCertMeta`, `WolfCertTransport`, `wolfcert_buffer_free` | | `wolfcert/keygen.h`, `wolfcert/csr.h` | Key generation, import / export, CSR building | | `wolfcert/store.h` | `WolfCertStoreOps` storage vtable, POSIX and in-memory backends | diff --git a/cli/wolfcert_client.c b/cli/wolfcert_client.c index e99cd09..4dd9cf6 100644 --- a/cli/wolfcert_client.c +++ b/cli/wolfcert_client.c @@ -1275,14 +1275,14 @@ static int scep_enroll(const Opts* opts, const WolfCertServerCfg* srv, renew_srv.proto_opts.scep.renewal_msg_type = WOLFCERT_SCEP_RENEWAL_MSG_PKCS_REQ; } - rc = wolfcert_scep_renewal_req_ex(&renew_srv, &caps, cas.ra, + rc = wolfcert_scep_renewal_req(&renew_srv, &caps, cas.ra, cas.ra_len, cas.bundle, cas.bundle_len, renew_cert->buffer, renew_cert->length, key, csr->data, csr->len, &scep_result); } else if (rc == WOLFCERT_OK) { - rc = wolfcert_scep_pkcs_req_ex(srv, &caps, cas.ra, cas.ra_len, - cas.bundle, cas.bundle_len, key, - csr->data, csr->len, &scep_result); + rc = wolfcert_scep_pkcs_req(srv, &caps, cas.ra, cas.ra_len, + cas.bundle, cas.bundle_len, key, + csr->data, csr->len, &scep_result); } /* GetCertInitial polling while PENDING (RFC 8894 section 3.3.3). */ @@ -1566,6 +1566,7 @@ static int cmd_enroll(int argc, char** argv) if (ret == 0 && p == WOLFCERT_PROTO_EST) { #ifdef WOLFCERT_HAVE_EST + WolfCertEstResult est_result = { 0 }; if (opts.pha) { /* TLS 1.3 post-handshake auth on one kept-alive session. */ srv.proto_opts.est.allow_post_handshake_auth = 1; @@ -1576,14 +1577,14 @@ static int cmd_enroll(int argc, char** argv) rc = wolfcert_est_session_get_cacerts(es, &ca_pem); wolfcert_buffer_free(&ca_pem); if (rc == WOLFCERT_OK) - rc = wolfcert_est_session_simple_enroll(es, csr.data, csr.len, &issued); + rc = wolfcert_est_session_simple_enroll(es, csr.data, csr.len, + &est_result); wolfcert_est_session_close(es); } } else { - /* _ex reports 202 Accepted (RFC 7030 section 4.2.3) as PENDING. */ - WolfCertEstResult est_result = { 0 }; - rc = wolfcert_est_simple_enroll_ex(&srv, csr.data, csr.len, &est_result); + /* 202 Accepted (RFC 7030 section 4.2.3) comes back as PENDING. */ + rc = wolfcert_est_simple_enroll(&srv, csr.data, csr.len, &est_result); int attempts = 0; while (rc == WOLFCERT_OK && est_result.status == WOLFCERT_EST_STATUS_PENDING && @@ -1599,26 +1600,26 @@ static int cmd_enroll(int argc, char** argv) nanosleep(&ts, NULL); wolfcert_est_result_free(&est_result); - rc = wolfcert_est_simple_enroll_ex(&srv, csr.data, csr.len, &est_result); + rc = wolfcert_est_simple_enroll(&srv, csr.data, csr.len, &est_result); attempts++; } + } - if (rc == WOLFCERT_OK) { - if (est_result.status == WOLFCERT_EST_STATUS_SUCCESS) { - issued = est_result.cert_pem; - est_result.cert_pem.data = NULL; - est_result.cert_pem.len = 0; - } - else if (est_result.status == WOLFCERT_EST_STATUS_PENDING) { - rc = WOLFCERT_ERR_PENDING; - } - else { - rc = WOLFCERT_ERR_PROTOCOL; - } + if (rc == WOLFCERT_OK) { + if (est_result.status == WOLFCERT_EST_STATUS_SUCCESS) { + issued = est_result.cert_pem; + est_result.cert_pem.data = NULL; + est_result.cert_pem.len = 0; + } + else if (est_result.status == WOLFCERT_EST_STATUS_PENDING) { + rc = WOLFCERT_ERR_PENDING; + } + else { + rc = WOLFCERT_ERR_PROTOCOL; } - - wolfcert_est_result_free(&est_result); } + + wolfcert_est_result_free(&est_result); #endif } else if (ret == 0) { diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index a8ecefc..dfed005 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -143,7 +143,7 @@ four endpoints a typical device needs: | `POST /simplereenroll` | `wolfcert_est_simple_reenroll` | Same, with the cert being renewed used as the implicit client identity. The test server's requirements for it are at `WolfCertServerCfgSrv.tls_client_ca_pem` in `wolfcert/server.h`. | A typical flow is: `wolfcert_key_generate` → `wolfcert_csr_build` → -`wolfcert_est_simple_enroll` → persist the returned PEM. The keep-alive +`wolfcert_est_simple_enroll` → persist the issued PEM. The keep-alive `WolfCertEstSession` carries several requests on one TLS connection, and is what enables the post-handshake-auth bootstrap below. @@ -186,10 +186,10 @@ A client that holds a cert but does not opt into PHA is refused, since the server no longer asks for it during a TLS 1.3 handshake. **Manual approval.** A server may park an enrollment with `202 Accepted` + -`Retry-After` (RFC 7030 §4.2.3), symmetrical to SCEP's PENDING. The `_ex` -enroll calls, one-shot and session, surface this as a `WolfCertEstResult` -with `UNSET` / `SUCCESS` / `FAILURE` / `PENDING` status and a -`retry_after_sec` hint; the simple-result calls flatten PENDING to +`Retry-After` (RFC 7030 §4.2.3), symmetrical to SCEP's PENDING. The enroll +calls, one-shot and session, surface this as a `WolfCertEstResult` with +`UNSET` / `SUCCESS` / `FAILURE` / `PENDING` status and a `retry_after_sec` +hint; `wolfcert_client_enroll` and `wolfcert_client_reenroll` return `WOLFCERT_ERR_PENDING`. **`/csrattrs` key-policy pinning.** `wolfcert_est_parse_csr_attrs` decodes the @@ -213,8 +213,8 @@ whose payload is an EnvelopedData, built on wolfSSL's `wc_PKCS7` API. | `GetCACaps` | plain text | `wolfcert_scep_get_ca_caps` | | `GetCACert` | cert body | `wolfcert_scep_get_ca_cert` | | `GetNextCACert` | cert body | `wolfcert_scep_get_next_ca_cert` | -| `PKCSReq` | 19 | `wolfcert_scep_pkcs_req_ex` (+ simple `_pkcs_req`) | -| `RenewalReq` | 17 | `wolfcert_scep_renewal_req_ex` (+ simple `_renewal_req`) | +| `PKCSReq` | 19 | `wolfcert_scep_pkcs_req` | +| `RenewalReq` | 17 | `wolfcert_scep_renewal_req` | | `GetCertInitial` | 20 | `wolfcert_scep_get_cert_initial` | | `GetCert` | 21 | `wolfcert_scep_get_cert` | @@ -249,7 +249,7 @@ 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, +(`wolfcert_scep_session_pkcs_req`/`_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 @@ -429,7 +429,7 @@ wolfSSL leaves `THREAD_LS_T` empty. Otherwise (RTOS and bare-metal builds) the state is one global, so keep wolfCert calls on one thread. After any failing call: ```c -int rc = wolfcert_est_simple_enroll(&srv, csr, csr_len, &cert); +int rc = wolfcert_est_simple_enroll(&srv, csr, csr_len, &res); if (rc != WOLFCERT_OK) { LOG("wolfCert: %s (ssl=%d): %s", wolfcert_strerror(rc), diff --git a/docs/MIGRATING-FROM-WOLFSCEP.md b/docs/MIGRATING-FROM-WOLFSCEP.md index 27a1116..0551a0d 100644 --- a/docs/MIGRATING-FROM-WOLFSCEP.md +++ b/docs/MIGRATING-FROM-WOLFSCEP.md @@ -38,7 +38,7 @@ If you must keep your own transport, for example on an RTOS with a proprietary s | `wolfSCEP_set_bundle` (the pkiMessage your application built) | nothing to pass: wolfCert builds the pkiMessage. You supply `csr_der`, `ra_cert` and `ca_bundle` and it does the rest | | `wolfSCEP_request(WS_REQUEST_CACAPS)` then `wolfSCEP_response` | `wolfcert_scep_get_ca_caps` | | `wolfSCEP_request(WS_REQUEST_CA)` then `wolfSCEP_response` | `wolfcert_scep_get_ca_cert` or `wolfcert_scep_get_ca_cert_enc` | -| `wolfSCEP_request(WS_REQUEST_ENROLL)` then `wolfSCEP_response` | `wolfcert_scep_pkcs_req` or `wolfcert_scep_pkcs_req_ex` | +| `wolfSCEP_request(WS_REQUEST_ENROLL)` then `wolfSCEP_response` | `wolfcert_scep_pkcs_req` | | `WS_POST_FLAG` | automatic: POST, or the RFC 8894 section 4.1 GET form, chosen from `GetCACaps` (see [`ARCHITECTURE.md`](ARCHITECTURE.md)) | | `wolfSCEP_reply_status` (the HTTP status code) | checked internally; a non-200 surfaces as `WOLFCERT_ERR_HTTP` | | your own pkiStatus parse against `WS_PKI_SUCCESS` / `WS_PKI_FAILURE` / `WS_PKI_PENDING` | `WolfCertScepResult.status`, `WOLFCERT_SCEP_STATUS_SUCCESS` / `_FAILURE` / `_PENDING` | @@ -109,10 +109,10 @@ wolfcert_csr_build(key, &meta, &csr); /* 4) PKCSReq. One call: envelope, sign, POST, verify, de-envelope. */ WolfCertScepResult res = { 0 }; -int rc = wolfcert_scep_pkcs_req_ex(&srv, &caps, - ca_der.data, ca_der.len, /* envelope target */ - ca_der.data, ca_der.len, /* trusted bundle */ - key, csr.data, csr.len, &res); +int rc = wolfcert_scep_pkcs_req(&srv, &caps, + ca_der.data, ca_der.len, /* envelope target */ + ca_der.data, ca_der.len, /* trusted bundle */ + key, csr.data, csr.len, &res); ``` `res.status` is then `SUCCESS` with the certificate in `res.cert_pem`, `PENDING` with the transactionID in `res.transaction_id` to poll with `wolfcert_scep_get_cert_initial`, or `FAILURE` with `res.fail_info` carrying the RFC 8894 failInfo. @@ -168,7 +168,7 @@ CAs that predate `RenewalReq` expect a renewal as messageType 19 signed by the o srv.proto_opts.scep.renewal_msg_type = WOLFCERT_SCEP_RENEWAL_MSG_PKCS_REQ; ``` -The default, `WOLFCERT_SCEP_RENEWAL_MSG_RENEWAL_REQ`, sends 17. The option is read by `wolfcert_scep_renewal_req_ex` and by the session renewals; initial enrollment is always messageType 19 with the self-signed signer and is unaffected. +The default, `WOLFCERT_SCEP_RENEWAL_MSG_RENEWAL_REQ`, sends 17. The option is read by `wolfcert_scep_renewal_req` and by the session renewals; initial enrollment is always messageType 19 with the self-signed signer and is unaffected. To decide which your CA wants, read `WolfCertScepCaps.renewal`, which `wolfcert_scep_get_ca_caps` fills from the GetCACaps `Renewal` keyword. A CA that advertises it accepts `RenewalReq`, so the default is right. If it does not advertise `Renewal` and your existing integration renews successfully anyway, you are relying on the older shape and want `PKCS_REQ`. wolfCert does not test the field for you. diff --git a/examples/enroll_cryptocb.c b/examples/enroll_cryptocb.c index 4a5f44f..ed92f09 100644 --- a/examples/enroll_cryptocb.c +++ b/examples/enroll_cryptocb.c @@ -79,16 +79,21 @@ int main(int argc, char** argv) return 2; WolfCertServerCfg srv = { .protocol = WOLFCERT_PROTO_EST, .server_url = argv[1] }; - WolfCertBuffer cert = { 0 }; - int rc = wolfcert_est_simple_enroll(&srv, csr.data, csr.len, &cert); + WolfCertEstResult res = { 0 }; + int rc = wolfcert_est_simple_enroll(&srv, csr.data, csr.len, &res); if (rc != WOLFCERT_OK) { fprintf(stderr, "enroll failed: %s\n", wolfcert_strerror(rc)); return 2; } - fwrite(cert.data, 1, cert.len, stdout); + if (res.status == WOLFCERT_EST_STATUS_PENDING) { + fprintf(stderr, "enroll pending; retry after %d s\n", + res.retry_after_sec); + return 2; + } + fwrite(res.cert_pem.data, 1, res.cert_pem.len, stdout); wolfcert_buffer_free(&csr); - wolfcert_buffer_free(&cert); + wolfcert_est_result_free(&res); wolfcert_key_free(key); wc_CryptoCb_UnRegisterDevice(MY_DEV_ID); wolfcert_cleanup(); diff --git a/examples/enroll_est.c b/examples/enroll_est.c index 67bc86d..cd56b57 100644 --- a/examples/enroll_est.c +++ b/examples/enroll_est.c @@ -98,17 +98,22 @@ int main(int argc, char** argv) .trust_anchors_len = trust_len, .verify_server = trust != NULL, }; - WolfCertBuffer cert = { 0 }; - int rc = wolfcert_est_simple_enroll(&srv, csr.data, csr.len, &cert); + WolfCertEstResult res = { 0 }; + int rc = wolfcert_est_simple_enroll(&srv, csr.data, csr.len, &res); if (rc != WOLFCERT_OK) { fprintf(stderr, "enroll failed: %s\n", wolfcert_strerror(rc)); return 2; } + if (res.status == WOLFCERT_EST_STATUS_PENDING) { + fprintf(stderr, "enroll pending; retry after %d s\n", + res.retry_after_sec); + return 2; + } - fwrite(cert.data, 1, cert.len, stdout); + fwrite(res.cert_pem.data, 1, res.cert_pem.len, stdout); wolfcert_buffer_free(&csr); - wolfcert_buffer_free(&cert); + wolfcert_est_result_free(&res); wolfcert_key_free(key); free(trust); wolfcert_cleanup(); diff --git a/examples/enroll_scep.c b/examples/enroll_scep.c index def2712..96891f7 100644 --- a/examples/enroll_scep.c +++ b/examples/enroll_scep.c @@ -121,20 +121,28 @@ int main(int argc, char** argv) if (wolfcert_csr_build(key, &meta, &csr) != WOLFCERT_OK) return 2; - /* 4) PKCSReq. */ - WolfCertBuffer cert = { 0 }; + /* 4) PKCSReq, enveloped to and verified against the single CA cert. */ + WolfCertScepResult res = { 0 }; rc = wolfcert_scep_pkcs_req(&srv, &caps, ca_der->buffer, ca_der->length, - key, csr.data, csr.len, &cert); + ca_der->buffer, ca_der->length, + key, csr.data, csr.len, &res); if (rc != WOLFCERT_OK) { fprintf(stderr, "PKCSReq failed: %s\n", wolfcert_strerror(rc)); return 2; } - fwrite(cert.data, 1, cert.len, stdout); + if (res.status != WOLFCERT_SCEP_STATUS_SUCCESS) { + fprintf(stderr, "PKCSReq %s\n", + res.status == WOLFCERT_SCEP_STATUS_PENDING + ? "pending; poll with wolfcert_scep_get_cert_initial" + : "rejected by the CA"); + return 2; + } + fwrite(res.cert_pem.data, 1, res.cert_pem.len, stdout); wc_FreeDer(&ca_der); wolfcert_buffer_free(&ca_pem); wolfcert_buffer_free(&csr); - wolfcert_buffer_free(&cert); + wolfcert_scep_result_free(&res); wolfcert_key_free(key); wolfcert_cleanup(); return 0; diff --git a/src/client.c b/src/client.c index 0eed18b..0bce577 100644 --- a/src/client.c +++ b/src/client.c @@ -29,6 +29,7 @@ #include "internal.h" #include +#include struct WolfCertClient { void* heap; }; @@ -137,6 +138,21 @@ static int wolfcert_client_auto_csrattrs(const WolfCertServerCfg* srv, wolfcert_csr_attrs_free(&attrs); return rc; } + +static int wolfcert_client_take_cert(int rc, WolfCertEstResult* r, + WolfCertBuffer* out_cert_pem) +{ + if (rc == WOLFCERT_OK && r->status == WOLFCERT_EST_STATUS_PENDING) { + rc = WOLFCERT_ERR_PENDING; + } + else if (rc == WOLFCERT_OK) { + *out_cert_pem = r->cert_pem; + memset(&r->cert_pem, 0, sizeof(r->cert_pem)); + } + + wolfcert_est_result_free(r); + return rc; +} #endif int wolfcert_client_enroll(WolfCertClient* client, const WolfCertServerCfg* srv, @@ -176,7 +192,10 @@ int wolfcert_client_enroll(WolfCertClient* client, const WolfCertServerCfg* srv, #ifdef WOLFCERT_HAVE_EST if (srv->protocol == WOLFCERT_PROTO_EST) { - rc = wolfcert_est_simple_enroll(srv, csr.data, csr.len, out_cert_pem); + WolfCertEstResult r; + + rc = wolfcert_est_simple_enroll(srv, csr.data, csr.len, &r); + rc = wolfcert_client_take_cert(rc, &r, out_cert_pem); } else #endif @@ -235,9 +254,11 @@ int wolfcert_client_reenroll(WolfCertClient* client, const WolfCertServerCfg* sr #ifdef WOLFCERT_HAVE_EST if (srv->protocol == WOLFCERT_PROTO_EST) { + WolfCertEstResult r; + rc = wolfcert_est_simple_reenroll(srv, current_cert, current_cert_len, - current_key, csr.data, csr.len, - out_cert_pem); + current_key, csr.data, csr.len, &r); + rc = wolfcert_client_take_cert(rc, &r, out_cert_pem); } else #endif diff --git a/src/errors.c b/src/errors.c index 6fae496..5098ece 100644 --- a/src/errors.c +++ b/src/errors.c @@ -49,7 +49,7 @@ const char* wolfcert_strerror(int err) case WOLFCERT_ERR_UNSUPPORTED: return "unsupported"; case WOLFCERT_ERR_PENDING: - return "pending (SCEP pkiStatus=3)"; + return "pending approval"; case WOLFCERT_ERR_WANT_READ: return "would block (want read)"; case WOLFCERT_ERR_WANT_WRITE: diff --git a/src/est/est_client.c b/src/est/est_client.c index 58982c0..298651a 100644 --- a/src/est/est_client.c +++ b/src/est/est_client.c @@ -441,10 +441,10 @@ int wolfcert_est_session_get_csr_attrs_nb(WolfCertEstSession* s, WOLFCERT_ENCODING_DER, out_attrs_der, NULL, 1); } -int wolfcert_est_session_simple_enroll_ex(WolfCertEstSession* s, - const uint8_t* csr_der, - size_t csr_der_len, - WolfCertEstResult* out) +int wolfcert_est_session_simple_enroll(WolfCertEstSession* s, + const uint8_t* csr_der, + size_t csr_der_len, + WolfCertEstResult* out) { if (out == NULL) return WOLFCERT_ERR_BAD_ARG; @@ -454,23 +454,10 @@ int wolfcert_est_session_simple_enroll_ex(WolfCertEstSession* s, WOLFCERT_ENCODING_PEM, &out->cert_pem, out, 0); } -int wolfcert_est_session_simple_enroll(WolfCertEstSession* s, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertBuffer* out_cert_pem) -{ - WolfCertEstResult r = { 0 }; - int rc = est_session_call(s, EST_OP_SIMPLE_ENROLL, csr_der, csr_der_len, - WOLFCERT_ENCODING_PEM, out_cert_pem, &r, 0); - - if (rc == WOLFCERT_OK && r.status == WOLFCERT_EST_STATUS_PENDING) - rc = WOLFCERT_ERR_PENDING; - return rc; -} - -int wolfcert_est_session_simple_enroll_nb_ex(WolfCertEstSession* s, - const uint8_t* csr_der, - size_t csr_der_len, - WolfCertEstResult* out) +int wolfcert_est_session_simple_enroll_nb(WolfCertEstSession* s, + const uint8_t* csr_der, + size_t csr_der_len, + WolfCertEstResult* out) { if (out == NULL) return WOLFCERT_ERR_BAD_ARG; @@ -480,24 +467,10 @@ int wolfcert_est_session_simple_enroll_nb_ex(WolfCertEstSession* s, WOLFCERT_ENCODING_PEM, &out->cert_pem, out, 1); } -int wolfcert_est_session_simple_enroll_nb(WolfCertEstSession* s, - const uint8_t* csr_der, - size_t csr_der_len, - WolfCertBuffer* out_cert_pem) -{ - WolfCertEstResult r = { 0 }; - int rc = est_session_call(s, EST_OP_SIMPLE_ENROLL, csr_der, csr_der_len, - WOLFCERT_ENCODING_PEM, out_cert_pem, &r, 1); - - if (rc == WOLFCERT_OK && r.status == WOLFCERT_EST_STATUS_PENDING) - rc = WOLFCERT_ERR_PENDING; - return rc; -} - -int wolfcert_est_session_simple_reenroll_ex(WolfCertEstSession* s, - const uint8_t* csr_der, - size_t csr_der_len, - WolfCertEstResult* out) +int wolfcert_est_session_simple_reenroll(WolfCertEstSession* s, + const uint8_t* csr_der, + size_t csr_der_len, + WolfCertEstResult* out) { if (out == NULL) return WOLFCERT_ERR_BAD_ARG; @@ -507,10 +480,10 @@ int wolfcert_est_session_simple_reenroll_ex(WolfCertEstSession* s, WOLFCERT_ENCODING_PEM, &out->cert_pem, out, 0); } -int wolfcert_est_session_simple_reenroll_nb_ex(WolfCertEstSession* s, - const uint8_t* csr_der, - size_t csr_der_len, - WolfCertEstResult* out) +int wolfcert_est_session_simple_reenroll_nb(WolfCertEstSession* s, + const uint8_t* csr_der, + size_t csr_der_len, + WolfCertEstResult* out) { if (out == NULL) return WOLFCERT_ERR_BAD_ARG; @@ -566,9 +539,9 @@ int wolfcert_est_get_csr_attrs(const WolfCertServerCfg* srv, WOLFCERT_ENCODING_DER, out_attrs_der, NULL); } -int wolfcert_est_simple_enroll_ex(const WolfCertServerCfg* srv, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertEstResult* out) +int wolfcert_est_simple_enroll(const WolfCertServerCfg* srv, + const uint8_t* csr_der, size_t csr_der_len, + WolfCertEstResult* out) { if (out == NULL) return WOLFCERT_ERR_BAD_ARG; @@ -578,24 +551,11 @@ int wolfcert_est_simple_enroll_ex(const WolfCertServerCfg* srv, WOLFCERT_ENCODING_PEM, &out->cert_pem, out); } -int wolfcert_est_simple_enroll(const WolfCertServerCfg* srv, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertBuffer* out_cert_pem) -{ - WolfCertEstResult r = { 0 }; - int rc = est_one_shot(srv, EST_OP_SIMPLE_ENROLL, csr_der, csr_der_len, - WOLFCERT_ENCODING_PEM, out_cert_pem, &r); - - if (rc == WOLFCERT_OK && r.status == WOLFCERT_EST_STATUS_PENDING) - rc = WOLFCERT_ERR_PENDING; - return rc; -} - -int wolfcert_est_simple_reenroll_ex(const WolfCertServerCfg* srv, - const uint8_t* current_cert, size_t current_cert_len, - const WolfCertKey* current_key, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertEstResult* out) +int wolfcert_est_simple_reenroll(const WolfCertServerCfg* srv, + const uint8_t* current_cert, size_t current_cert_len, + const WolfCertKey* current_key, + const uint8_t* csr_der, size_t csr_der_len, + WolfCertEstResult* out) { WolfCertServerCfg cfg; WolfCertBuffer key_pem = { 0 }; @@ -626,29 +586,3 @@ int wolfcert_est_simple_reenroll_ex(const WolfCertServerCfg* srv, wolfcert_buffer_free(&key_pem); return rc; } - -int wolfcert_est_simple_reenroll(const WolfCertServerCfg* srv, - const uint8_t* current_cert, size_t current_cert_len, - const WolfCertKey* current_key, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertBuffer* out_cert_pem) -{ - WolfCertEstResult r; - int rc; - - if (out_cert_pem == NULL) - return WOLFCERT_ERR_BAD_ARG; - - rc = wolfcert_est_simple_reenroll_ex(srv, current_cert, current_cert_len, - current_key, csr_der, csr_der_len, &r); - if (rc == WOLFCERT_OK && r.status == WOLFCERT_EST_STATUS_PENDING) { - rc = WOLFCERT_ERR_PENDING; - } - else if (rc == WOLFCERT_OK) { - *out_cert_pem = r.cert_pem; - memset(&r.cert_pem, 0, sizeof(r.cert_pem)); - } - - wolfcert_est_result_free(&r); - return rc; -} diff --git a/src/scep/scep_client.c b/src/scep/scep_client.c index aa55f25..2e791e6 100644 --- a/src/scep/scep_client.c +++ b/src/scep/scep_client.c @@ -964,13 +964,13 @@ static int rsa_key_to_der(const WolfCertKey* key, void* heap, return WOLFCERT_OK; } -int wolfcert_scep_pkcs_req_ex(const WolfCertServerCfg* srv, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const WolfCertKey* new_key, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertScepResult* out) +int wolfcert_scep_pkcs_req(const WolfCertServerCfg* srv, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const WolfCertKey* new_key, + const uint8_t* csr_der, size_t csr_der_len, + WolfCertScepResult* out) { if (out == NULL) return WOLFCERT_ERR_BAD_ARG; @@ -1020,49 +1020,14 @@ int wolfcert_scep_pkcs_req_ex(const WolfCertServerCfg* srv, return rc; } -int wolfcert_scep_pkcs_req(const WolfCertServerCfg* srv, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const WolfCertKey* new_key, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertBuffer* out_cert_pem) -{ - if (out_cert_pem == NULL) - return WOLFCERT_ERR_BAD_ARG; - - /* ra_cert doubles as the one-cert trust bundle. */ - WolfCertScepResult r = { 0 }; - int rc = wolfcert_scep_pkcs_req_ex(srv, caps, ra_cert, ra_cert_len, - ra_cert, ra_cert_len, - new_key, csr_der, csr_der_len, &r); - if (rc != WOLFCERT_OK) { - wolfcert_scep_result_free(&r); - return rc; - } - - if (r.status == WOLFCERT_SCEP_STATUS_SUCCESS) { - *out_cert_pem = r.cert_pem; - r.cert_pem.data = NULL; - r.cert_pem.len = 0; - wolfcert_scep_result_free(&r); - return WOLFCERT_OK; - } - - int err = r.status == WOLFCERT_SCEP_STATUS_PENDING - ? WOLFCERT_ERR_PENDING : WOLFCERT_ERR_PROTOCOL; - - wolfcert_scep_result_free(&r); - return err; -} - -int wolfcert_scep_renewal_req_ex(const WolfCertServerCfg* srv, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const uint8_t* current_cert, size_t current_cert_len, - const WolfCertKey* current_key, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertScepResult* out) +int wolfcert_scep_renewal_req(const WolfCertServerCfg* srv, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const uint8_t* current_cert, size_t current_cert_len, + const WolfCertKey* current_key, + const uint8_t* csr_der, size_t csr_der_len, + WolfCertScepResult* out) { if (out == NULL) return WOLFCERT_ERR_BAD_ARG; @@ -1105,45 +1070,6 @@ int wolfcert_scep_renewal_req_ex(const WolfCertServerCfg* srv, return rc; } -int wolfcert_scep_renewal_req(const WolfCertServerCfg* srv, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* current_cert, size_t current_cert_len, - const WolfCertKey* current_key, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertBuffer* out_cert_pem) -{ - if (out_cert_pem == NULL) - return WOLFCERT_ERR_BAD_ARG; - - /* ra_cert doubles as the one-cert trust bundle. */ - WolfCertScepResult r = { 0 }; - int rc = wolfcert_scep_renewal_req_ex(srv, caps, ra_cert, ra_cert_len, - ra_cert, ra_cert_len, - current_cert, current_cert_len, - current_key, - csr_der, csr_der_len, &r); - - if (rc != WOLFCERT_OK) { - wolfcert_scep_result_free(&r); - return rc; - } - - if (r.status == WOLFCERT_SCEP_STATUS_SUCCESS) { - *out_cert_pem = r.cert_pem; - r.cert_pem.data = NULL; - r.cert_pem.len = 0; - wolfcert_scep_result_free(&r); - return WOLFCERT_OK; - } - - int err = r.status == WOLFCERT_SCEP_STATUS_PENDING - ? WOLFCERT_ERR_PENDING : WOLFCERT_ERR_PROTOCOL; - - wolfcert_scep_result_free(&r); - return err; -} - int wolfcert_scep_get_cert_initial(const WolfCertServerCfg* srv, const WolfCertScepCaps* caps, const uint8_t* ra_cert, size_t ra_cert_len, @@ -1191,8 +1117,8 @@ int wolfcert_scep_get_cert_initial(const WolfCertServerCfg* srv, if (rc == WOLFCERT_OK) rc = rsa_key_to_der(signer_key, heap, &key_der, &key_der_len); - /* A pending PKCSReq has no signer cert; regenerate the transient - * self-signed one that pkcs_req_ex used. */ + /* A pending PKCSReq has no signer cert; rebuild the transient + * self-signed one from signer_key. */ if (rc == WOLFCERT_OK && signer_cert == NULL) { rc = wolfcert_scep_self_signed_rsa((RsaKey*)signer_key->impl, csr_der, csr_der_len, @@ -1848,12 +1774,12 @@ static int scep_session_begin_get_cert_initial(WolfCertScepSession* s, return rc; } -int wolfcert_scep_session_pkcs_req_ex(WolfCertScepSession* s, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const WolfCertKey* new_key, const uint8_t* csr_der, size_t csr_der_len, - WolfCertScepResult* out) +int wolfcert_scep_session_pkcs_req(WolfCertScepSession* s, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const WolfCertKey* new_key, const uint8_t* csr_der, size_t csr_der_len, + WolfCertScepResult* out) { if (out == NULL) return WOLFCERT_ERR_BAD_ARG; @@ -1871,7 +1797,7 @@ int wolfcert_scep_session_pkcs_req_ex(WolfCertScepSession* s, if (s->nonblocking) return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", - "blocking _ex call on an async session; use the _nb variant"); + "blocking call on an async session; use the _nb variant"); int rc = scep_session_begin_pkcs_req(s, caps, ra_cert, ra_cert_len, ca_bundle, ca_bundle_len, @@ -1901,7 +1827,7 @@ int wolfcert_scep_session_pkcs_req_nb(WolfCertScepSession* s, if (!s->nonblocking) return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", - "non-blocking _nb call on a blocking session; use the _ex variant"); + "non-blocking _nb call on a blocking session; use the blocking variant"); if (!s->in_active) { if (caps == NULL || ra_cert == NULL || ra_cert_len == 0 || @@ -1922,13 +1848,13 @@ int wolfcert_scep_session_pkcs_req_nb(WolfCertScepSession* s, return scep_session_drive_nb(s); } -int wolfcert_scep_session_renewal_req_ex(WolfCertScepSession* s, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const uint8_t* current_cert, size_t current_cert_len, - const WolfCertKey* current_key, const uint8_t* csr_der, size_t csr_der_len, - WolfCertScepResult* out) +int wolfcert_scep_session_renewal_req(WolfCertScepSession* s, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const uint8_t* current_cert, size_t current_cert_len, + const WolfCertKey* current_key, const uint8_t* csr_der, size_t csr_der_len, + WolfCertScepResult* out) { if (out == NULL) return WOLFCERT_ERR_BAD_ARG; @@ -1946,7 +1872,7 @@ int wolfcert_scep_session_renewal_req_ex(WolfCertScepSession* s, if (s->nonblocking) return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", - "blocking _ex call on an async session; use the _nb variant"); + "blocking call on an async session; use the _nb variant"); int rc = scep_session_begin_renewal(s, caps, ra_cert, ra_cert_len, ca_bundle, ca_bundle_len, @@ -1978,7 +1904,7 @@ int wolfcert_scep_session_renewal_req_nb(WolfCertScepSession* s, if (!s->nonblocking) return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", - "non-blocking _nb call on a blocking session; use the _ex variant"); + "non-blocking _nb call on a blocking session; use the blocking variant"); if (!s->in_active) { if (caps == NULL || ra_cert == NULL || ra_cert_len == 0 || ca_bundle == NULL || @@ -2000,15 +1926,15 @@ int wolfcert_scep_session_renewal_req_nb(WolfCertScepSession* s, return scep_session_drive_nb(s); } -int wolfcert_scep_session_get_cert_initial_ex(WolfCertScepSession* s, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const uint8_t* signer_cert, size_t signer_cert_len, - const WolfCertKey* signer_key, - const uint8_t* csr_der, size_t csr_der_len, - const uint8_t* transaction_id, size_t transaction_id_len, - WolfCertScepResult* out) +int wolfcert_scep_session_get_cert_initial(WolfCertScepSession* s, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const uint8_t* signer_cert, size_t signer_cert_len, + const WolfCertKey* signer_key, + const uint8_t* csr_der, size_t csr_der_len, + const uint8_t* transaction_id, size_t transaction_id_len, + WolfCertScepResult* out) { if (out == NULL) return WOLFCERT_ERR_BAD_ARG; @@ -2026,7 +1952,7 @@ int wolfcert_scep_session_get_cert_initial_ex(WolfCertScepSession* s, if (s->nonblocking) return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", - "blocking _ex call on an async session; use the _nb variant"); + "blocking call on an async session; use the _nb variant"); int rc = scep_session_begin_get_cert_initial(s, caps, ra_cert, ra_cert_len, ca_bundle, ca_bundle_len, signer_cert, signer_cert_len, signer_key, @@ -2059,7 +1985,7 @@ int wolfcert_scep_session_get_cert_initial_nb(WolfCertScepSession* s, if (!s->nonblocking) return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", - "non-blocking _nb call on a blocking session; use the _ex variant"); + "non-blocking _nb call on a blocking session; use the blocking variant"); if (!s->in_active) { if (caps == NULL || ra_cert == NULL || ra_cert_len == 0 || ca_bundle == NULL || diff --git a/tests/integration/est_client_cases.h b/tests/integration/est_client_cases.h index 81aa9a6..5faec44 100644 --- a/tests/integration/est_client_cases.h +++ b/tests/integration/est_client_cases.h @@ -57,7 +57,7 @@ static inline int enroll_one(const WolfCertServerCfg* client_cfg, .san_dns = dns, .san_dns_len = 1 }; WolfCertKey* dk = NULL; WolfCertBuffer csr = { 0 }; - WolfCertBuffer issued = { 0 }; + WolfCertEstResult issued = { 0 }; DerBuffer* issued_der = NULL; WOLFSSL_CERT_MANAGER* cm = NULL; int rc = WOLFCERT_OK; @@ -74,8 +74,8 @@ static inline int enroll_one(const WolfCertServerCfg* client_cfg, } } EST_CHECK(rc == WOLFCERT_OK); - EST_CHECK(wc_PemToDer(issued.data, (long)issued.len, CERT_TYPE, - &issued_der, NULL, NULL, NULL) == 0); + EST_CHECK(wc_PemToDer(issued.cert_pem.data, (long)issued.cert_pem.len, + CERT_TYPE, &issued_der, NULL, NULL, NULL) == 0); if (ret == 0) cm = wolfSSL_CertManagerNew(); EST_CHECK(cm != NULL); @@ -90,7 +90,7 @@ static inline int enroll_one(const WolfCertServerCfg* client_cfg, wolfSSL_CertManagerFree(cm); wc_FreeDer(&issued_der); - wolfcert_buffer_free(&issued); + wolfcert_est_result_free(&issued); wolfcert_buffer_free(&csr); wolfcert_key_free(dk); return ret; @@ -125,7 +125,7 @@ static inline int enroll_check_san(const WolfCertServerCfg* client_cfg) const byte ip[] = { 10, 20, 30, 40 }; WolfCertKey* dk = NULL; WolfCertBuffer csr = { 0 }; - WolfCertBuffer issued = { 0 }; + WolfCertEstResult issued = { 0 }; DerBuffer* der = NULL; DecodedCert dc; int dc_init = 0; @@ -145,8 +145,8 @@ static inline int enroll_check_san(const WolfCertServerCfg* client_cfg) } } EST_CHECK(rc == WOLFCERT_OK); - EST_CHECK(wc_PemToDer(issued.data, (long)issued.len, CERT_TYPE, &der, - NULL, NULL, NULL) == 0); + EST_CHECK(wc_PemToDer(issued.cert_pem.data, (long)issued.cert_pem.len, + CERT_TYPE, &der, NULL, NULL, NULL) == 0); if (ret == 0) { wc_InitDecodedCert(&dc, der->buffer, der->length, NULL); dc_init = 1; @@ -163,14 +163,14 @@ static inline int enroll_check_san(const WolfCertServerCfg* client_cfg) if (dc_init) wc_FreeDecodedCert(&dc); wc_FreeDer(&der); - wolfcert_buffer_free(&issued); + wolfcert_est_result_free(&issued); wolfcert_buffer_free(&csr); wolfcert_key_free(dk); return ret; } /* HTTP Basic (RFC 7030 section 3.2.3) on a keep-alive session; a wrong - * password fails both the plain and _ex enroll. */ + * password fails the enroll. */ static inline int session_basic_auth(const WolfCertServerCfg* client_cfg) { WolfCertKeyCfg kcfg = { .type = TEST_ENROLL_KEY_TYPE, .param = TEST_ENROLL_KEY_PARAM, @@ -180,8 +180,7 @@ static inline int session_basic_auth(const WolfCertServerCfg* client_cfg) WolfCertKey* dk = NULL; WolfCertBuffer csr = { 0 }; WolfCertBuffer ca_pem = { 0 }; - WolfCertBuffer issued = { 0 }; - WolfCertBuffer bad_out = { 0 }; + WolfCertEstResult issued = { 0 }; WolfCertEstResult bad_res = { 0 }; WolfCertEstSession* s = NULL; WolfCertEstSession* bs = NULL; @@ -215,20 +214,20 @@ static inline int session_basic_auth(const WolfCertServerCfg* client_cfg) EST_CHECK(ca_pem.len > 0); EST_CHECK(wolfcert_est_session_simple_enroll(s, csr.data, csr.len, &issued) == WOLFCERT_OK); - EST_CHECK(wc_PemToDer(issued.data, (long)issued.len, CERT_TYPE, - &issued_der, NULL, NULL, NULL) == 0); + EST_CHECK(wc_PemToDer(issued.cert_pem.data, (long)issued.cert_pem.len, + CERT_TYPE, &issued_der, NULL, NULL, NULL) == 0); /* The target heap is too small for two open sessions. */ wc_FreeDer(&issued_der); wolfcert_buffer_free(&ca_pem); - wolfcert_buffer_free(&issued); + wolfcert_est_result_free(&issued); wolfcert_est_session_close(s); s = NULL; EST_CHECK(wolfcert_est_session_open(&bad_cfg, &bs) == WOLFCERT_OK); if (ret == 0) { brc = wolfcert_est_session_simple_enroll(bs, csr.data, csr.len, - &bad_out); + &bad_res); if (brc != WOLFCERT_ERR_AUTH) { fprintf(stderr, "bad-password enroll: rc=%d (%s) wolfssl=%d %s\n", brc, wolfcert_strerror(brc), wolfcert_last_wolfssl_err(), @@ -236,22 +235,11 @@ static inline int session_basic_auth(const WolfCertServerCfg* client_cfg) } } EST_CHECK(brc == WOLFCERT_ERR_AUTH); - EST_CHECK(bad_out.data == NULL); - wolfcert_est_session_close(bs); - bs = NULL; - - brc = WOLFCERT_OK; - EST_CHECK(wolfcert_est_session_open(&bad_cfg, &bs) == WOLFCERT_OK); - if (ret == 0) - brc = wolfcert_est_session_simple_enroll_ex(bs, csr.data, csr.len, - &bad_res); - EST_CHECK(brc == WOLFCERT_ERR_AUTH); EST_CHECK(bad_res.status == WOLFCERT_EST_STATUS_FAILURE); EST_CHECK(bad_res.cert_pem.data == NULL); wolfcert_est_session_close(bs); wolfcert_est_result_free(&bad_res); - wolfcert_buffer_free(&bad_out); wolfcert_buffer_free(&csr); wolfcert_key_free(dk); return ret; diff --git a/tests/integration/test_est_async_roundtrip.c b/tests/integration/test_est_async_roundtrip.c index 3ad334b..eab41a0 100644 --- a/tests/integration/test_est_async_roundtrip.c +++ b/tests/integration/test_est_async_roundtrip.c @@ -80,31 +80,12 @@ static int pump_get_cacerts(WolfCertEstSession* s, WolfCertBuffer* out) } } -/* Returns the terminal rc. */ static int pump_simple_enroll(WolfCertEstSession* s, const uint8_t* csr, size_t csr_len, - WolfCertBuffer* out) + WolfCertEstResult* out) { for (;;) { int rc = wolfcert_est_session_simple_enroll_nb(s, csr, csr_len, out); - if (rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE) { - if (wait_io(s, rc) != 0) - return WOLFCERT_ERR_IO; - continue; - } - if (rc != WOLFCERT_OK) - fprintf(stderr, "enroll rc=%d (%s) last=%s\n", - rc, wolfcert_strerror(rc), wolfcert_last_error_message()); - return rc; - } -} - -static int pump_simple_enroll_ex(WolfCertEstSession* s, - const uint8_t* csr, size_t csr_len, - WolfCertEstResult* out) -{ - for (;;) { - int rc = wolfcert_est_session_simple_enroll_nb_ex(s, csr, csr_len, out); if (rc != WOLFCERT_ERR_WANT_READ && rc != WOLFCERT_ERR_WANT_WRITE) return rc; if (wait_io(s, rc) != 0) @@ -183,12 +164,14 @@ int main(void) REQUIRE(ca_pem.len > 0); /* The server sends a PHA CertificateRequest mid-call. */ - WolfCertBuffer issued = { 0 }; + WolfCertEstResult issued = { 0 }; REQUIRE(pump_simple_enroll(es, csr.data, csr.len, &issued) == 0); - REQUIRE(memmem(issued.data, issued.len, "BEGIN CERTIFICATE", 17) != NULL); + REQUIRE(issued.status == WOLFCERT_EST_STATUS_SUCCESS); + REQUIRE(memmem(issued.cert_pem.data, issued.cert_pem.len, + "BEGIN CERTIFICATE", 17) != NULL); wolfcert_buffer_free(&ca_pem); - wolfcert_buffer_free(&issued); + wolfcert_est_result_free(&issued); wolfcert_est_session_close(es); wolfcert_server_stop(srv); @@ -227,24 +210,27 @@ int main(void) WolfCertEstSession* bes = NULL; REQUIRE(wolfcert_est_session_open_async(&bcli, &bes) == WOLFCERT_OK); - WolfCertBuffer bca = { 0 }, bissued = { 0 }; + WolfCertBuffer bca = { 0 }; + WolfCertEstResult bissued = { 0 }; REQUIRE(pump_get_cacerts(bes, &bca) == 0); REQUIRE(bca.len > 0); REQUIRE(pump_simple_enroll(bes, csr.data, csr.len, &bissued) == 0); - REQUIRE(memmem(bissued.data, bissued.len, "BEGIN CERTIFICATE", 17) != NULL); + REQUIRE(bissued.status == WOLFCERT_EST_STATUS_SUCCESS); + REQUIRE(memmem(bissued.cert_pem.data, bissued.cert_pem.len, + "BEGIN CERTIFICATE", 17) != NULL); wolfcert_buffer_free(&bca); - wolfcert_buffer_free(&bissued); + wolfcert_est_result_free(&bissued); wolfcert_est_session_close(bes); - /* A wrong password fails both enroll forms, one session each. */ + /* A wrong password fails the enroll. */ WolfCertServerCfg wcli = bcli; wcli.proto_opts.est.password = "wrong"; bes = NULL; REQUIRE(wolfcert_est_session_open_async(&wcli, &bes) == WOLFCERT_OK); WolfCertEstResult wr = { 0 }; - REQUIRE(pump_simple_enroll_ex(bes, csr.data, csr.len, &wr) + REQUIRE(pump_simple_enroll(bes, csr.data, csr.len, &wr) == WOLFCERT_ERR_AUTH); REQUIRE(wr.status == WOLFCERT_EST_STATUS_FAILURE); REQUIRE(wr.cert_pem.data == NULL); @@ -256,13 +242,6 @@ int main(void) wolfcert_buffer_free(&bca); wolfcert_est_session_close(bes); - bes = NULL; - REQUIRE(wolfcert_est_session_open_async(&wcli, &bes) == WOLFCERT_OK); - REQUIRE(pump_simple_enroll(bes, csr.data, csr.len, &bissued) - == WOLFCERT_ERR_AUTH); - REQUIRE(bissued.data == NULL); - wolfcert_est_session_close(bes); - wolfcert_server_stop(bsrv); pthread_join(btid, NULL); wolfcert_server_free(bsrv); diff --git a/tests/integration/test_est_mldsa_roundtrip.c b/tests/integration/test_est_mldsa_roundtrip.c index 6d6b325..b935967 100644 --- a/tests/integration/test_est_mldsa_roundtrip.c +++ b/tests/integration/test_est_mldsa_roundtrip.c @@ -73,15 +73,15 @@ static int enroll_mldsa(const WolfCertServerCfg* client_cfg, WolfCertKeyType kt, WolfCertBuffer csr = { 0 }; REQUIRE(wolfcert_csr_build(dk, &meta, &csr) == WOLFCERT_OK); - WolfCertBuffer issued = { 0 }; + WolfCertEstResult issued = { 0 }; REQUIRE(wolfcert_est_simple_enroll(client_cfg, csr.data, csr.len, &issued) == WOLFCERT_OK); - REQUIRE(issued.len > 0); - size_t pem_len = issued.len; + REQUIRE(issued.cert_pem.len > 0); + size_t pem_len = issued.cert_pem.len; DerBuffer* issued_der = NULL; - REQUIRE(wc_PemToDer(issued.data, (long)issued.len, CERT_TYPE, - &issued_der, NULL, NULL, NULL) == 0); + REQUIRE(wc_PemToDer(issued.cert_pem.data, (long)issued.cert_pem.len, + CERT_TYPE, &issued_der, NULL, NULL, NULL) == 0); WOLFSSL_CERT_MANAGER* cm = wolfSSL_CertManagerNew(); REQUIRE(cm != NULL); REQUIRE(wolfSSL_CertManagerLoadCABuffer(cm, ca_pem->data, (long)ca_pem->len, @@ -95,7 +95,7 @@ static int enroll_mldsa(const WolfCertServerCfg* client_cfg, WolfCertKeyType kt, wc_FreeDer(&issued_der); wolfcert_buffer_free(&csr); - wolfcert_buffer_free(&issued); + wolfcert_est_result_free(&issued); wolfcert_key_free(dk); printf(" %s: enrolled + verified (%zu byte PEM)\n", label, pem_len); return 0; diff --git a/tests/integration/test_est_mtls_roundtrip.c b/tests/integration/test_est_mtls_roundtrip.c index b2580ed..99391c4 100644 --- a/tests/integration/test_est_mtls_roundtrip.c +++ b/tests/integration/test_est_mtls_roundtrip.c @@ -75,7 +75,7 @@ static int test_reenroll_ignores_cfg_identity(const char* url, WolfCertKey* cur_key = NULL; WolfCertKey* dk = NULL; WolfCertBuffer csr = { 0 }; - WolfCertBuffer issued = { 0 }; + WolfCertEstResult issued = { 0 }; WolfCertBuffer ca_pem = { 0 }; WolfCertEstSession* sess = NULL; WolfCertEstResult res = { 0 }; @@ -111,8 +111,9 @@ static int test_reenroll_ignores_cfg_identity(const char* url, csr.data, csr.len, &issued); if (rc != WOLFCERT_OK) fprintf(stderr, "mtls reenroll rc=%d (%s)\n", rc, wolfcert_strerror(rc)); - REQUIRE(rc == WOLFCERT_OK); - REQUIRE(memmem(issued.data, issued.len, "BEGIN CERTIFICATE", 17) != NULL); + REQUIRE(rc == WOLFCERT_OK && issued.status == WOLFCERT_EST_STATUS_SUCCESS); + REQUIRE(memmem(issued.cert_pem.data, issued.cert_pem.len, + "BEGIN CERTIFICATE", 17) != NULL); /* A session reenrolls with the identity it was opened with. */ cli.client_cert = cli_cert; @@ -120,21 +121,21 @@ static int test_reenroll_ignores_cfg_identity(const char* url, cli.client_key = cli_key; cli.client_key_len = cli_key_len; REQUIRE(wolfcert_est_session_open(&cli, &sess) == WOLFCERT_OK); - rc = wolfcert_est_session_simple_reenroll_ex(sess, csr.data, csr.len, &res); + rc = wolfcert_est_session_simple_reenroll(sess, csr.data, csr.len, &res); wolfcert_est_session_close(sess); REQUIRE(rc == WOLFCERT_OK && res.status == WOLFCERT_EST_STATUS_SUCCESS); wolfcert_est_result_free(&res); REQUIRE(wolfcert_est_session_open_async(&cli, &sess) == WOLFCERT_OK); - while ((rc = wolfcert_est_session_simple_reenroll_nb_ex(sess, csr.data, - csr.len, &res)) + while ((rc = wolfcert_est_session_simple_reenroll_nb(sess, csr.data, + csr.len, &res)) == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE) REQUIRE(wait_io(sess, rc) == 0); wolfcert_est_session_close(sess); REQUIRE(rc == WOLFCERT_OK && res.status == WOLFCERT_EST_STATUS_SUCCESS); wolfcert_est_result_free(&res); - wolfcert_buffer_free(&issued); + wolfcert_est_result_free(&issued); wolfcert_buffer_free(&csr); wolfcert_key_free(dk); wolfcert_key_free(cur_key); @@ -170,7 +171,7 @@ static int test_reenroll_rejects_identity_change(const char* url, for (size_t i = 0; i < sizeof(metas) / sizeof(metas[0]); ++i) { WolfCertBuffer csr = { 0 }; - WolfCertBuffer issued = { 0 }; + WolfCertEstResult issued = { 0 }; int rc; REQUIRE(wolfcert_csr_build(dk, &metas[i], &csr) == WOLFCERT_OK); @@ -180,7 +181,7 @@ static int test_reenroll_rejects_identity_change(const char* url, if (rc == WOLFCERT_OK) fprintf(stderr, "reenroll with changed identity %zu was issued\n", i); REQUIRE(rc == WOLFCERT_ERR_HTTP); - REQUIRE(issued.data == NULL); + REQUIRE(issued.cert_pem.data == NULL); } wolfcert_key_free(dk); @@ -203,7 +204,7 @@ static int test_reenroll_rejects_non_csr(const char* url, .verify_server = 1, }; WolfCertKey* cur_key = NULL; - WolfCertBuffer issued = { 0 }; + WolfCertEstResult issued = { 0 }; int rc; REQUIRE(wolfcert_key_from_pem(cli_key, cli_key_len, NULL, &cur_key) == WOLFCERT_OK); @@ -214,7 +215,7 @@ static int test_reenroll_rejects_non_csr(const char* url, fprintf(stderr, "non-CSR reenroll rc=%d (%s)\n", rc, wolfcert_last_error_message()); REQUIRE(rc == WOLFCERT_ERR_HTTP); - REQUIRE(issued.data == NULL); + REQUIRE(issued.cert_pem.data == NULL); REQUIRE(strstr(wolfcert_last_error_message(), "HTTP 400") != NULL); return 0; } @@ -325,9 +326,9 @@ static int test_reenroll_server_issued(const uint8_t* tls_cert, size_t tls_cert_ WolfCertServer* srv = NULL; WolfCertBuffer ca_der = { 0 }; WolfCertBuffer csr = { 0 }; - WolfCertBuffer issued = { 0 }; - WolfCertBuffer renewed = { 0 }; - WolfCertBuffer san_changed = { 0 }; + WolfCertEstResult issued = { 0 }; + WolfCertEstResult renewed = { 0 }; + WolfCertEstResult san_changed = { 0 }; WolfCertKey* k1 = NULL; WolfCertKey* k2 = NULL; uint8_t* bundle = NULL; @@ -338,7 +339,7 @@ static int test_reenroll_server_issued(const uint8_t* tls_cert, size_t tls_cert_ int rc; #ifdef HAVE_ECC uint8_t crit_csr[4096]; - WolfCertBuffer refused = { 0 }; + WolfCertEstResult refused = { 0 }; int crit_len; #endif @@ -381,43 +382,44 @@ static int test_reenroll_server_issued(const uint8_t* tls_cert, size_t tls_cert_ REQUIRE(wolfcert_csr_build(k1, &meta, &csr) == WOLFCERT_OK); rc = wolfcert_est_simple_enroll(&cli, csr.data, csr.len, &issued); wolfcert_buffer_free(&csr); - REQUIRE(rc == WOLFCERT_OK); + REQUIRE(rc == WOLFCERT_OK && issued.status == WOLFCERT_EST_STATUS_SUCCESS); REQUIRE(wolfcert_key_generate(&kcfg, &k2) == WOLFCERT_OK); REQUIRE(wolfcert_csr_build(k2, &meta, &csr) == WOLFCERT_OK); - rc = wolfcert_est_simple_reenroll(&cli, issued.data, issued.len, k1, + rc = wolfcert_est_simple_reenroll(&cli, issued.cert_pem.data, issued.cert_pem.len, k1, csr.data, csr.len, &renewed); if (rc != WOLFCERT_OK) fprintf(stderr, "reenroll of a server-issued cert rc=%d (%s)\n", rc, wolfcert_strerror(rc)); - REQUIRE(rc == WOLFCERT_OK); - REQUIRE(memmem(renewed.data, renewed.len, "BEGIN CERTIFICATE", 17) != NULL); + REQUIRE(rc == WOLFCERT_OK && renewed.status == WOLFCERT_EST_STATUS_SUCCESS); + REQUIRE(memmem(renewed.cert_pem.data, renewed.cert_pem.len, + "BEGIN CERTIFICATE", 17) != NULL); /* Same Subject and SAN shape, one dNSName changed. */ wolfcert_buffer_free(&csr); REQUIRE(wolfcert_csr_build(k2, &meta2, &csr) == WOLFCERT_OK); - rc = wolfcert_est_simple_reenroll(&cli, issued.data, issued.len, k1, + rc = wolfcert_est_simple_reenroll(&cli, issued.cert_pem.data, issued.cert_pem.len, k1, csr.data, csr.len, &san_changed); REQUIRE(rc == WOLFCERT_ERR_HTTP); REQUIRE(strstr(wolfcert_last_error_message(), "HTTP 400") != NULL); - REQUIRE(san_changed.data == NULL); + REQUIRE(san_changed.cert_pem.data == NULL); #ifdef HAVE_ECC - crit_len = csr_with_critical_san(&issued, crit_csr, sizeof(crit_csr)); + crit_len = csr_with_critical_san(&issued.cert_pem, crit_csr, sizeof(crit_csr)); REQUIRE(crit_len > 0); - rc = wolfcert_est_simple_reenroll(&cli, issued.data, issued.len, k1, + rc = wolfcert_est_simple_reenroll(&cli, issued.cert_pem.data, issued.cert_pem.len, k1, crit_csr, (size_t)crit_len, &refused); REQUIRE(rc == WOLFCERT_ERR_HTTP); REQUIRE(strstr(wolfcert_last_error_message(), "HTTP 400") != NULL); - REQUIRE(refused.data == NULL); + REQUIRE(refused.cert_pem.data == NULL); #endif wolfcert_server_stop(srv); pthread_join(tid, NULL); wolfcert_server_free(srv); wolfcert_store_memory_close(store); - wolfcert_buffer_free(&renewed); - wolfcert_buffer_free(&issued); + wolfcert_est_result_free(&renewed); + wolfcert_est_result_free(&issued); wolfcert_buffer_free(&csr); wolfcert_buffer_free(&ca_der); wolfcert_key_free(k2); @@ -427,13 +429,16 @@ static int test_reenroll_server_issued(const uint8_t* tls_cert, size_t tls_cert_ } /* The approval gate runs after the identity check, so a mismatched reenroll - * gets its 400 on the first POST rather than a 202. */ -static int test_reenroll_mismatch_not_parked(const uint8_t* tls_cert, size_t tls_cert_len, - const uint8_t* tls_key, size_t tls_key_len, - const uint8_t* cli_cert, size_t cli_cert_len, - const uint8_t* cli_key, size_t cli_key_len) + * gets its 400 on the first POST rather than a 202. A matching one is parked + * and issued on its re-POST. */ +static int test_reenroll_under_approval(const uint8_t* tls_cert, size_t tls_cert_len, + const uint8_t* tls_key, size_t tls_key_len, + const uint8_t* cli_cert, size_t cli_cert_len, + const uint8_t* cli_key, size_t cli_key_len) { const WolfCertCertMeta meta = { .subject_dn = "CN=someone-else" }; + const WolfCertCertMeta same = { .subject_dn = "CN=factory-bootstrap" }; + const WolfCertCertMeta none = { 0 }; WolfCertKeyCfg kcfg = { .type = TEST_ENROLL_KEY_TYPE, .param = TEST_ENROLL_KEY_PARAM, .dev_id = WOLFCERT_DEVID_SOFTWARE }; WolfCertServerCfgSrv cfg = { @@ -450,7 +455,10 @@ static int test_reenroll_mismatch_not_parked(const uint8_t* tls_cert, size_t tls WolfCertKey* cur_key = NULL; WolfCertKey* dk = NULL; WolfCertBuffer csr = { 0 }; - WolfCertBuffer issued = { 0 }; + WolfCertEstResult issued = { 0 }; + WolfCertClient* client = NULL; + WolfCertKey* nk = NULL; + WolfCertBuffer cert = { 0 }; pthread_t tid; char url[128]; int rc; @@ -477,8 +485,30 @@ static int test_reenroll_mismatch_not_parked(const uint8_t* tls_cert, size_t tls fprintf(stderr, "mismatched reenroll under approval rc=%d (%s)\n", rc, wolfcert_last_error_message()); REQUIRE(rc == WOLFCERT_ERR_HTTP); - REQUIRE(issued.data == NULL); + REQUIRE(issued.cert_pem.data == NULL); REQUIRE(strstr(wolfcert_last_error_message(), "HTTP 400") != NULL); + wolfcert_buffer_free(&csr); + + REQUIRE(wolfcert_csr_build(dk, &same, &csr) == WOLFCERT_OK); + REQUIRE(wolfcert_est_simple_reenroll(&cli, cli_cert, cli_cert_len, cur_key, + csr.data, csr.len, &issued) == WOLFCERT_OK); + REQUIRE(issued.status == WOLFCERT_EST_STATUS_PENDING); + REQUIRE(issued.retry_after_sec == 1); + REQUIRE(issued.cert_pem.data == NULL); + REQUIRE(wolfcert_est_simple_reenroll(&cli, cli_cert, cli_cert_len, cur_key, + csr.data, csr.len, &issued) == WOLFCERT_OK); + REQUIRE(issued.status == WOLFCERT_EST_STATUS_SUCCESS); + REQUIRE(memmem(issued.cert_pem.data, issued.cert_pem.len, + "BEGIN CERTIFICATE", 17) != NULL); + wolfcert_est_result_free(&issued); + + /* The client API reports the 202 as WOLFCERT_ERR_PENDING. */ + REQUIRE(wolfcert_client_new(&client) == WOLFCERT_OK); + REQUIRE(wolfcert_client_reenroll(client, &cli, cli_cert, cli_cert_len, + cur_key, &kcfg, &none, &nk, &cert) + == WOLFCERT_ERR_PENDING); + REQUIRE(nk == NULL && cert.data == NULL); + wolfcert_client_free(client); wolfcert_server_stop(srv); pthread_join(tid, NULL); @@ -511,7 +541,7 @@ static int test_reenroll_needs_peer_cert(const char* url, WolfCertKey* cur_key = NULL; WolfCertKey* dk = NULL; WolfCertBuffer csr = { 0 }; - WolfCertBuffer issued = { 0 }; + WolfCertEstResult issued = { 0 }; int rc; REQUIRE(wolfcert_key_from_pem(cli_key, cli_key_len, NULL, &cur_key) == WOLFCERT_OK); @@ -524,7 +554,7 @@ static int test_reenroll_needs_peer_cert(const char* url, wolfcert_key_free(cur_key); REQUIRE(rc == WOLFCERT_ERR_HTTP); REQUIRE(strstr(wolfcert_last_error_message(), "HTTP 500") != NULL); - REQUIRE(issued.data == NULL); + REQUIRE(issued.cert_pem.data == NULL); return 0; } #endif @@ -586,8 +616,7 @@ int main(void) WolfCertEstSession* ns = NULL; WolfCertEstResult nr = { 0 }; REQUIRE(wolfcert_est_session_open(&cli, &ns) == WOLFCERT_OK); - REQUIRE(wolfcert_est_session_simple_reenroll_ex(ns, csr, sizeof(csr), - &nr) + REQUIRE(wolfcert_est_session_simple_reenroll(ns, csr, sizeof(csr), &nr) == WOLFCERT_ERR_BAD_ARG); wolfcert_est_session_close(ns); } @@ -618,15 +647,16 @@ int main(void) WolfCertBuffer csr = { 0 }; REQUIRE(wolfcert_csr_build(dk, &meta, &csr) == WOLFCERT_OK); - WolfCertBuffer issued = { 0 }; + WolfCertEstResult issued = { 0 }; int rc = wolfcert_est_simple_enroll(&cli, csr.data, csr.len, &issued); if (rc != WOLFCERT_OK) fprintf(stderr, "mtls enroll rc=%d (%s)\n", rc, wolfcert_strerror(rc)); - REQUIRE(rc == WOLFCERT_OK); - REQUIRE(memmem(issued.data, issued.len, "BEGIN CERTIFICATE", 17) != NULL); + REQUIRE(rc == WOLFCERT_OK && issued.status == WOLFCERT_EST_STATUS_SUCCESS); + REQUIRE(memmem(issued.cert_pem.data, issued.cert_pem.len, + "BEGIN CERTIFICATE", 17) != NULL); wolfcert_buffer_free(&csr); - wolfcert_buffer_free(&issued); + wolfcert_est_result_free(&issued); wolfcert_key_free(dk); } @@ -656,10 +686,10 @@ int main(void) tls_key, tls_key_len, cli_cert, cli_cert_len, cli_key, cli_key_len) == 0); - REQUIRE(test_reenroll_mismatch_not_parked(tls_cert, tls_cert_len, - tls_key, tls_key_len, - cli_cert, cli_cert_len, - cli_key, cli_key_len) == 0); + REQUIRE(test_reenroll_under_approval(tls_cert, tls_cert_len, + tls_key, tls_key_len, + cli_cert, cli_cert_len, + cli_key, cli_key_len) == 0); #endif free(tls_cert); diff --git a/tests/integration/test_est_pending_roundtrip.c b/tests/integration/test_est_pending_roundtrip.c index 465148a..59bcf13 100644 --- a/tests/integration/test_est_pending_roundtrip.c +++ b/tests/integration/test_est_pending_roundtrip.c @@ -67,9 +67,8 @@ static int wait_io(WolfCertEstSession* s, int rc) return poll(&p, 1, 5000) > 0 ? 0 : -1; } -/* Returns the terminal rc, WOLFCERT_ERR_PENDING included. */ static int pump_enroll_nb(WolfCertEstSession* s, const uint8_t* csr, - size_t csr_len, WolfCertBuffer* out) + size_t csr_len, WolfCertEstResult* out) { for (;;) { int rc = wolfcert_est_session_simple_enroll_nb(s, csr, csr_len, out); @@ -80,18 +79,6 @@ static int pump_enroll_nb(WolfCertEstSession* s, const uint8_t* csr, } } -static int pump_enroll_nb_ex(WolfCertEstSession* s, const uint8_t* csr, - size_t csr_len, WolfCertEstResult* out) -{ - for (;;) { - int rc = wolfcert_est_session_simple_enroll_nb_ex(s, csr, csr_len, out); - if (rc != WOLFCERT_ERR_WANT_READ && rc != WOLFCERT_ERR_WANT_WRITE) - return rc; - if (wait_io(s, rc) != 0) - return WOLFCERT_ERR_IO; - } -} - static int pump_cacerts_nb(WolfCertEstSession* s, WolfCertBuffer* out) { for (;;) { @@ -155,13 +142,13 @@ static int pending_path(WolfCertServer* s) WolfCertBuffer ca_pem = { 0 }; REQUIRE(wolfcert_est_get_cacerts(&cli, &ca_pem) == WOLFCERT_OK); - /* _ex reports PENDING with the Retry-After hint, then SUCCESS. */ + /* The one-shot reports PENDING with the Retry-After hint, then SUCCESS. */ WolfCertKey* dk_ex = NULL; WolfCertBuffer csr_ex = { 0 }; REQUIRE(make_csr("CN=device-est-pending-ex", &dk_ex, &csr_ex) == 0); WolfCertEstResult r1 = { 0 }; - int rc = wolfcert_est_simple_enroll_ex(&cli, csr_ex.data, csr_ex.len, &r1); + int rc = wolfcert_est_simple_enroll(&cli, csr_ex.data, csr_ex.len, &r1); REQUIRE(rc == WOLFCERT_OK); REQUIRE(r1.status == WOLFCERT_EST_STATUS_PENDING); REQUIRE(r1.retry_after_sec == 1); @@ -169,7 +156,7 @@ static int pending_path(WolfCertServer* s) wolfcert_est_result_free(&r1); WolfCertEstResult r2 = { 0 }; - rc = wolfcert_est_simple_enroll_ex(&cli, csr_ex.data, csr_ex.len, &r2); + rc = wolfcert_est_simple_enroll(&cli, csr_ex.data, csr_ex.len, &r2); REQUIRE(rc == WOLFCERT_OK); REQUIRE(r2.status == WOLFCERT_EST_STATUS_SUCCESS); REQUIRE(r2.cert_pem.data != NULL); @@ -197,14 +184,14 @@ static int pending_path(WolfCertServer* s) * FAILURE on the first POST. */ static const uint8_t not_a_csr[] = "this is not a PKCS#10 request"; WolfCertEstResult bad = { 0 }; - rc = wolfcert_est_simple_enroll_ex(&cli, not_a_csr, sizeof(not_a_csr), - &bad); + rc = wolfcert_est_simple_enroll(&cli, not_a_csr, sizeof(not_a_csr), + &bad); REQUIRE(rc == WOLFCERT_ERR_HTTP); REQUIRE(bad.status == WOLFCERT_EST_STATUS_FAILURE); wolfcert_est_result_free(&bad); csr_ex.data[csr_ex.len - 1] ^= 0x01; - rc = wolfcert_est_simple_enroll_ex(&cli, csr_ex.data, csr_ex.len, &bad); + rc = wolfcert_est_simple_enroll(&cli, csr_ex.data, csr_ex.len, &bad); REQUIRE(rc == WOLFCERT_ERR_HTTP); REQUIRE(bad.status == WOLFCERT_EST_STATUS_FAILURE); wolfcert_est_result_free(&bad); @@ -229,86 +216,21 @@ static int pending_path(WolfCertServer* s) wolfcert_buffer_free(&csr_ex); wolfcert_key_free(dk_ex); - /* wolfcert_est_simple_enroll: ERR_PENDING first, the cert on the retry. */ - WolfCertKey* dk_leg = NULL; - WolfCertBuffer csr_leg = { 0 }; - REQUIRE(make_csr("CN=device-est-pending-legacy", &dk_leg, &csr_leg) == 0); - - WolfCertBuffer legacy_out = { 0 }; - int legacy_rc = wolfcert_est_simple_enroll(&cli, csr_leg.data, csr_leg.len, - &legacy_out); - REQUIRE(legacy_rc == WOLFCERT_ERR_PENDING); - REQUIRE(legacy_out.data == NULL); - - legacy_rc = wolfcert_est_simple_enroll(&cli, csr_leg.data, csr_leg.len, - &legacy_out); - REQUIRE(legacy_rc == WOLFCERT_OK); - REQUIRE(legacy_out.data != NULL); - REQUIRE(memmem(legacy_out.data, legacy_out.len, - "BEGIN CERTIFICATE", 17) != NULL); - wolfcert_buffer_free(&legacy_out); - - wolfcert_buffer_free(&csr_leg); - wolfcert_key_free(dk_leg); - - /* Session enroll: ERR_PENDING first, the cert on the retry. */ - WolfCertKey* dk_sess = NULL; - WolfCertBuffer csr_sess = { 0 }; - REQUIRE(make_csr("CN=device-est-pending-session", &dk_sess, &csr_sess) == 0); - - WolfCertEstSession* sess = NULL; - REQUIRE(wolfcert_est_session_open(&cli, &sess) == WOLFCERT_OK); - - WolfCertBuffer sess_out = { 0 }; - int sess_rc = wolfcert_est_session_simple_enroll(sess, csr_sess.data, - csr_sess.len, &sess_out); - REQUIRE(sess_rc == WOLFCERT_ERR_PENDING); - REQUIRE(sess_out.data == NULL); - - sess_rc = wolfcert_est_session_simple_enroll(sess, csr_sess.data, - csr_sess.len, &sess_out); - REQUIRE(sess_rc == WOLFCERT_OK); - REQUIRE(sess_out.data != NULL); - REQUIRE(memmem(sess_out.data, sess_out.len, - "BEGIN CERTIFICATE", 17) != NULL); - wolfcert_buffer_free(&sess_out); - - wolfcert_est_session_close(sess); - wolfcert_buffer_free(&csr_sess); - wolfcert_key_free(dk_sess); - - /* Non-blocking session enroll, retried on the same connection. */ - WolfCertKey* dk_async = NULL; - WolfCertBuffer csr_async = { 0 }; - REQUIRE(make_csr("CN=device-est-pending-async", &dk_async, &csr_async) == 0); - - WolfCertEstSession* asess = NULL; - REQUIRE(wolfcert_est_session_open_async(&cli, &asess) == WOLFCERT_OK); - - WolfCertBuffer async_out = { 0 }; - int async_rc = pump_enroll_nb(asess, csr_async.data, csr_async.len, - &async_out); - REQUIRE(async_rc == WOLFCERT_ERR_PENDING); - REQUIRE(async_out.data == NULL); - - async_rc = pump_enroll_nb(asess, csr_async.data, csr_async.len, &async_out); - REQUIRE(async_rc == WOLFCERT_OK); - REQUIRE(async_out.data != NULL); - REQUIRE(memmem(async_out.data, async_out.len, - "BEGIN CERTIFICATE", 17) != NULL); - wolfcert_buffer_free(&async_out); - - /* A blocking call works on the same session from _open_async. */ - WolfCertBuffer async_ca = { 0 }; - REQUIRE(wolfcert_est_session_get_cacerts(asess, &async_ca) == WOLFCERT_OK); - REQUIRE(async_ca.len > 0); - wolfcert_buffer_free(&async_ca); - - wolfcert_est_session_close(asess); - wolfcert_buffer_free(&csr_async); - wolfcert_key_free(dk_async); - - /* Session _ex carries the Retry-After hint. */ + /* wolfcert_client_enroll reports a 202 as WOLFCERT_ERR_PENDING. */ + WolfCertClient* client = NULL; + REQUIRE(wolfcert_client_new(&client) == WOLFCERT_OK); + WolfCertKeyCfg kcfg = { .type = TEST_ENROLL_KEY_TYPE, .param = TEST_ENROLL_KEY_PARAM, + .dev_id = WOLFCERT_DEVID_SOFTWARE }; + WolfCertCertMeta meta = { .subject_dn = "CN=device-est-pending-client" }; + WolfCertKey* key = NULL; + WolfCertBuffer cert = { 0 }; + rc = wolfcert_client_enroll(client, &cli, &kcfg, &meta, &key, &cert); + wolfcert_client_free(client); + REQUIRE(rc == WOLFCERT_ERR_PENDING); + REQUIRE(key == NULL); + REQUIRE(cert.data == NULL && cert.len == 0); + + /* A session enroll carries the Retry-After hint. */ WolfCertKey* dk_sex = NULL; WolfCertBuffer csr_sex = { 0 }; REQUIRE(make_csr("CN=device-est-pending-session-ex", &dk_sex, &csr_sex) == 0); @@ -318,14 +240,14 @@ static int pending_path(WolfCertServer* s) /* A missing CSR is refused on a live session, which stays usable. */ WolfCertEstResult sr1 = { 0 }; - REQUIRE(wolfcert_est_session_simple_enroll_ex(xsess, NULL, csr_sex.len, - &sr1) == WOLFCERT_ERR_BAD_ARG); - REQUIRE(wolfcert_est_session_simple_enroll_ex(xsess, csr_sex.data, 0, - &sr1) == WOLFCERT_ERR_BAD_ARG); + REQUIRE(wolfcert_est_session_simple_enroll(xsess, NULL, csr_sex.len, + &sr1) == WOLFCERT_ERR_BAD_ARG); + REQUIRE(wolfcert_est_session_simple_enroll(xsess, csr_sex.data, 0, + &sr1) == WOLFCERT_ERR_BAD_ARG); REQUIRE(sr1.status == WOLFCERT_EST_STATUS_UNSET); - rc = wolfcert_est_session_simple_enroll_ex(xsess, csr_sex.data, - csr_sex.len, &sr1); + rc = wolfcert_est_session_simple_enroll(xsess, csr_sex.data, + csr_sex.len, &sr1); REQUIRE(rc == WOLFCERT_OK); REQUIRE(sr1.status == WOLFCERT_EST_STATUS_PENDING); REQUIRE(sr1.retry_after_sec == 1); @@ -333,8 +255,8 @@ static int pending_path(WolfCertServer* s) wolfcert_est_result_free(&sr1); WolfCertEstResult sr2 = { 0 }; - rc = wolfcert_est_session_simple_enroll_ex(xsess, csr_sex.data, - csr_sex.len, &sr2); + rc = wolfcert_est_session_simple_enroll(xsess, csr_sex.data, + csr_sex.len, &sr2); REQUIRE(rc == WOLFCERT_OK); REQUIRE(sr2.status == WOLFCERT_EST_STATUS_SUCCESS); REQUIRE(sr2.cert_pem.data != NULL); @@ -345,7 +267,7 @@ static int pending_path(WolfCertServer* s) /* An _nb enroll on a session from wolfcert_est_session_open() runs to * PENDING (the issued CSR is queued again), then a blocking call works. */ WolfCertEstResult sr3 = { 0 }; - REQUIRE(pump_enroll_nb_ex(xsess, csr_sex.data, csr_sex.len, &sr3) + REQUIRE(pump_enroll_nb(xsess, csr_sex.data, csr_sex.len, &sr3) == WOLFCERT_OK); REQUIRE(sr3.status == WOLFCERT_EST_STATUS_PENDING); REQUIRE(sr3.retry_after_sec == 1); @@ -361,7 +283,7 @@ static int pending_path(WolfCertServer* s) wolfcert_buffer_free(&csr_sex); wolfcert_key_free(dk_sex); - /* Non-blocking session _ex: PENDING, retry_after_sec 1, then SUCCESS. */ + /* Non-blocking session enroll: PENDING, retry_after_sec 1, then SUCCESS. */ WolfCertKey* dk_nex = NULL; WolfCertBuffer csr_nex = { 0 }; REQUIRE(make_csr("CN=device-est-pending-async-ex", &dk_nex, &csr_nex) == 0); @@ -370,7 +292,7 @@ static int pending_path(WolfCertServer* s) REQUIRE(wolfcert_est_session_open_async(&cli, &nsess) == WOLFCERT_OK); WolfCertEstResult nr1 = { 0 }; - rc = pump_enroll_nb_ex(nsess, csr_nex.data, csr_nex.len, &nr1); + rc = pump_enroll_nb(nsess, csr_nex.data, csr_nex.len, &nr1); REQUIRE(rc == WOLFCERT_OK); REQUIRE(nr1.status == WOLFCERT_EST_STATUS_PENDING); REQUIRE(nr1.retry_after_sec == 1); @@ -378,7 +300,7 @@ static int pending_path(WolfCertServer* s) wolfcert_est_result_free(&nr1); WolfCertEstResult nr2 = { 0 }; - rc = pump_enroll_nb_ex(nsess, csr_nex.data, csr_nex.len, &nr2); + rc = pump_enroll_nb(nsess, csr_nex.data, csr_nex.len, &nr2); REQUIRE(rc == WOLFCERT_OK); REQUIRE(nr2.status == WOLFCERT_EST_STATUS_SUCCESS); REQUIRE(nr2.cert_pem.data != NULL); @@ -386,6 +308,12 @@ static int pending_path(WolfCertServer* s) "BEGIN CERTIFICATE", 17) != NULL); wolfcert_est_result_free(&nr2); + /* A blocking call works on the same session from _open_async. */ + WolfCertBuffer async_ca = { 0 }; + REQUIRE(wolfcert_est_session_get_cacerts(nsess, &async_ca) == WOLFCERT_OK); + REQUIRE(async_ca.len > 0); + wolfcert_buffer_free(&async_ca); + wolfcert_est_session_close(nsess); wolfcert_buffer_free(&csr_nex); wolfcert_key_free(dk_nex); @@ -414,8 +342,8 @@ static int pending_path(WolfCertServer* s) REQUIRE(rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE); WolfCertEstResult mr = { 0 }; - REQUIRE(wolfcert_est_session_simple_enroll_nb_ex(msess, csr_mix.data, - csr_mix.len, &mr) + REQUIRE(wolfcert_est_session_simple_enroll_nb(msess, csr_mix.data, + csr_mix.len, &mr) == WOLFCERT_ERR_BAD_ARG); REQUIRE(mr.status == WOLFCERT_EST_STATUS_UNSET); REQUIRE(mr.cert_pem.data == NULL); @@ -424,13 +352,10 @@ static int pending_path(WolfCertServer* s) == WOLFCERT_ERR_BAD_ARG); /* Blocking session requests are refused as well. */ - REQUIRE(wolfcert_est_session_simple_enroll_ex(msess, csr_mix.data, - csr_mix.len, &mr) - == WOLFCERT_ERR_BAD_ARG); - REQUIRE(mr.status == WOLFCERT_EST_STATUS_UNSET); REQUIRE(wolfcert_est_session_simple_enroll(msess, csr_mix.data, - csr_mix.len, &other_ca) + csr_mix.len, &mr) == WOLFCERT_ERR_BAD_ARG); + REQUIRE(mr.status == WOLFCERT_EST_STATUS_UNSET); REQUIRE(wolfcert_est_session_get_cacerts(msess, &other_ca) == WOLFCERT_ERR_BAD_ARG); REQUIRE(other_ca.data == NULL); @@ -447,16 +372,16 @@ static int pending_path(WolfCertServer* s) REQUIRE(wolfcert_est_session_open_async(&bh_cli, &msess) == WOLFCERT_OK); /* A missing CSR is refused without starting a request. */ - REQUIRE(wolfcert_est_session_simple_enroll_nb_ex(msess, NULL, csr_mix.len, - &mr) == WOLFCERT_ERR_BAD_ARG); - REQUIRE(wolfcert_est_session_simple_enroll_nb_ex(msess, csr_mix.data, 0, - &mr) == WOLFCERT_ERR_BAD_ARG); + REQUIRE(wolfcert_est_session_simple_enroll_nb(msess, NULL, csr_mix.len, + &mr) == WOLFCERT_ERR_BAD_ARG); + REQUIRE(wolfcert_est_session_simple_enroll_nb(msess, csr_mix.data, 0, + &mr) == WOLFCERT_ERR_BAD_ARG); REQUIRE(mr.status == WOLFCERT_EST_STATUS_UNSET); /* *out comes back zeroed on a WANT_* return. */ memset(&mr, 0xA5, sizeof(mr)); - rc = wolfcert_est_session_simple_enroll_nb_ex(msess, csr_mix.data, - csr_mix.len, &mr); + rc = wolfcert_est_session_simple_enroll_nb(msess, csr_mix.data, + csr_mix.len, &mr); REQUIRE(rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE); REQUIRE(mr.status == WOLFCERT_EST_STATUS_UNSET); REQUIRE(mr.cert_pem.data == NULL && mr.cert_pem.len == 0); @@ -466,17 +391,12 @@ static int pending_path(WolfCertServer* s) == WOLFCERT_ERR_BAD_ARG); REQUIRE(mix_ca.data == NULL); WolfCertEstResult other_r = { 0 }; - REQUIRE(wolfcert_est_session_simple_enroll_nb_ex(msess, csr_mix.data, - csr_mix.len, &other_r) - == WOLFCERT_ERR_BAD_ARG); - WolfCertBuffer other_pem = { 0 }; REQUIRE(wolfcert_est_session_simple_enroll_nb(msess, csr_mix.data, - csr_mix.len, &other_pem) + csr_mix.len, &other_r) == WOLFCERT_ERR_BAD_ARG); - REQUIRE(other_pem.data == NULL); - rc = wolfcert_est_session_simple_enroll_nb_ex(msess, csr_mix.data, - csr_mix.len, &mr); + rc = wolfcert_est_session_simple_enroll_nb(msess, csr_mix.data, + csr_mix.len, &mr); REQUIRE(rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE); wolfcert_est_session_close(msess); close(bh_fd); @@ -484,7 +404,7 @@ static int pending_path(WolfCertServer* s) /* A finished enroll frees the session for /cacerts. */ msess = NULL; REQUIRE(wolfcert_est_session_open_async(&cli, &msess) == WOLFCERT_OK); - REQUIRE(pump_enroll_nb_ex(msess, csr_mix.data, csr_mix.len, &mr) + REQUIRE(pump_enroll_nb(msess, csr_mix.data, csr_mix.len, &mr) == WOLFCERT_OK); REQUIRE(mr.status == WOLFCERT_EST_STATUS_PENDING); wolfcert_est_result_free(&mr); diff --git a/tests/integration/test_est_pha_roundtrip.c b/tests/integration/test_est_pha_roundtrip.c index b431d83..727a0b7 100644 --- a/tests/integration/test_est_pha_roundtrip.c +++ b/tests/integration/test_est_pha_roundtrip.c @@ -80,11 +80,11 @@ static int expect_enroll_refused(const WolfCertServerCfg* cli, const char* cn) WolfCertBuffer csr = { 0 }; REQUIRE(wolfcert_csr_build(dk, &meta, &csr) == WOLFCERT_OK); - WolfCertBuffer issued = { 0 }; + WolfCertEstResult issued = { 0 }; int rc = wolfcert_est_session_simple_enroll(s, csr.data, csr.len, &issued); REQUIRE(rc == WOLFCERT_ERR_AUTH); wolfcert_buffer_free(&csr); - wolfcert_buffer_free(&issued); + wolfcert_est_result_free(&issued); wolfcert_key_free(dk); wolfcert_est_session_close(s); return 0; @@ -187,23 +187,27 @@ int main(void) WolfCertBuffer csr = { 0 }; REQUIRE(wolfcert_csr_build(dk, &meta, &csr) == WOLFCERT_OK); - WolfCertBuffer issued = { 0 }; + WolfCertEstResult issued = { 0 }; int rc = wolfcert_est_session_simple_enroll(s, csr.data, csr.len, &issued); if (rc != WOLFCERT_OK) fprintf(stderr, "pha enroll rc=%d (%s) last=%s\n", rc, wolfcert_strerror(rc), wolfcert_last_error_message()); REQUIRE(rc == WOLFCERT_OK); - REQUIRE(memmem(issued.data, issued.len, "BEGIN CERTIFICATE", 17) != NULL); - wolfcert_buffer_free(&issued); + REQUIRE(issued.status == WOLFCERT_EST_STATUS_SUCCESS); + REQUIRE(memmem(issued.cert_pem.data, issued.cert_pem.len, + "BEGIN CERTIFICATE", 17) != NULL); + wolfcert_est_result_free(&issued); /* A second enroll reuses the cert the connection already holds. */ REQUIRE(wolfcert_est_session_simple_enroll(s, csr.data, csr.len, &issued) == WOLFCERT_OK); - REQUIRE(memmem(issued.data, issued.len, "BEGIN CERTIFICATE", 17) != NULL); + REQUIRE(issued.status == WOLFCERT_EST_STATUS_SUCCESS); + REQUIRE(memmem(issued.cert_pem.data, issued.cert_pem.len, + "BEGIN CERTIFICATE", 17) != NULL); wolfcert_buffer_free(&ca_pem); wolfcert_buffer_free(&csr); - wolfcert_buffer_free(&issued); + wolfcert_est_result_free(&issued); wolfcert_key_free(dk); wolfcert_est_session_close(s); } @@ -262,8 +266,8 @@ int main(void) REQUIRE(wolfcert_key_generate(&kcfg, &dk) == WOLFCERT_OK); REQUIRE(wolfcert_csr_build(dk, &meta, &csr) == WOLFCERT_OK); REQUIRE(wolfcert_est_session_open(&cli, &s) == WOLFCERT_OK); - REQUIRE(wolfcert_est_session_simple_reenroll_ex(s, csr.data, csr.len, - &issued) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_simple_reenroll(s, csr.data, csr.len, + &issued) == WOLFCERT_OK); REQUIRE(issued.status == WOLFCERT_EST_STATUS_SUCCESS); REQUIRE(memmem(issued.cert_pem.data, issued.cert_pem.len, "BEGIN CERTIFICATE", 17) != NULL); diff --git a/tests/integration/test_est_roundtrip.c b/tests/integration/test_est_roundtrip.c index 4339c53..fc0bbc3 100644 --- a/tests/integration/test_est_roundtrip.c +++ b/tests/integration/test_est_roundtrip.c @@ -113,7 +113,7 @@ static int enroll_raw_csr(const WolfCertServerCfg* client_cfg, test_signkey key; Cert req; byte csr[8192]; - WolfCertBuffer issued = { 0 }; + WolfCertEstResult issued = { 0 }; int sz; REQUIRE(wc_InitRng(&rng) == 0); @@ -133,13 +133,13 @@ static int enroll_raw_csr(const WolfCertServerCfg* client_cfg, == want_rc); if (want_rc == WOLFCERT_OK) { - REQUIRE(wc_PemToDer(issued.data, (long)issued.len, CERT_TYPE, der, - NULL, NULL, NULL) == 0); + REQUIRE(wc_PemToDer(issued.cert_pem.data, (long)issued.cert_pem.len, + CERT_TYPE, der, NULL, NULL, NULL) == 0); wc_InitDecodedCert(dc, (*der)->buffer, (*der)->length, NULL); REQUIRE(wc_ParseCert(dc, CERT_TYPE, NO_VERIFY, NULL) == 0); } - wolfcert_buffer_free(&issued); + wolfcert_est_result_free(&issued); test_signkey_free(&key); wc_FreeRng(&rng); return 0; @@ -188,13 +188,13 @@ static int enroll_check_subject_rdns(const WolfCertServerCfg* client_cfg) WolfCertBuffer csr = { 0 }; REQUIRE(wolfcert_csr_build(dk, &meta, &csr) == WOLFCERT_OK); - WolfCertBuffer issued = { 0 }; + WolfCertEstResult issued = { 0 }; REQUIRE(wolfcert_est_simple_enroll(client_cfg, csr.data, csr.len, &issued) == WOLFCERT_OK); DerBuffer* der = NULL; - REQUIRE(wc_PemToDer(issued.data, (long)issued.len, CERT_TYPE, &der, - NULL, NULL, NULL) == 0); + REQUIRE(wc_PemToDer(issued.cert_pem.data, (long)issued.cert_pem.len, + CERT_TYPE, &der, NULL, NULL, NULL) == 0); DecodedCert dc; wc_InitDecodedCert(&dc, der->buffer, der->length, NULL); @@ -220,7 +220,7 @@ static int enroll_check_subject_rdns(const WolfCertServerCfg* client_cfg) wc_FreeDecodedCert(&dc); wc_FreeDer(&der); wolfcert_buffer_free(&csr); - wolfcert_buffer_free(&issued); + wolfcert_est_result_free(&issued); wolfcert_key_free(dk); return 0; } @@ -332,10 +332,10 @@ int main(void) REQUIRE(pop_csr.len > 0); pop_csr.data[pop_csr.len - 1] ^= 0x01; - WolfCertBuffer pop_out = { 0 }; + WolfCertEstResult pop_out = { 0 }; REQUIRE(wolfcert_est_simple_enroll(&client_cfg, pop_csr.data, pop_csr.len, &pop_out) != WOLFCERT_OK); - REQUIRE(pop_out.data == NULL); + REQUIRE(pop_out.cert_pem.data == NULL); wolfcert_buffer_free(&pop_csr); wolfcert_key_free(pop_dk); @@ -350,7 +350,7 @@ int main(void) client_cfg.proto_opts.est.username = "bad"; client_cfg.proto_opts.est.password = "wrong"; - WolfCertBuffer bad = { 0 }; + WolfCertEstResult bad = { 0 }; REQUIRE(wolfcert_est_simple_enroll(&client_cfg, csr.data, csr.len, &bad) == WOLFCERT_ERR_AUTH); @@ -390,13 +390,13 @@ int main(void) WolfCertBuffer acsr = { 0 }; REQUIRE(wolfcert_csr_build(adk, &ameta, &acsr) == WOLFCERT_OK); - WolfCertBuffer aok = { 0 }; + WolfCertEstResult aok = { 0 }; REQUIRE(wolfcert_est_simple_enroll(&acli, acsr.data, acsr.len, &aok) - == WOLFCERT_OK); - wolfcert_buffer_free(&aok); + == WOLFCERT_OK && aok.status == WOLFCERT_EST_STATUS_SUCCESS); + wolfcert_est_result_free(&aok); acli.proto_opts.est.password = "hunterABC"; - WolfCertBuffer abad = { 0 }; + WolfCertEstResult abad = { 0 }; REQUIRE(wolfcert_est_simple_enroll(&acli, acsr.data, acsr.len, &abad) == WOLFCERT_ERR_AUTH); diff --git a/tests/integration/test_est_tls_roundtrip.c b/tests/integration/test_est_tls_roundtrip.c index d79e021..624ebc1 100644 --- a/tests/integration/test_est_tls_roundtrip.c +++ b/tests/integration/test_est_tls_roundtrip.c @@ -395,13 +395,14 @@ int main(void) WolfCertBuffer csr = { 0 }; REQUIRE(wolfcert_csr_build(dk, &meta, &csr) == WOLFCERT_OK); - WolfCertBuffer issued = { 0 }; + WolfCertEstResult issued = { 0 }; int rc = wolfcert_est_simple_enroll(&cli, csr.data, csr.len, &issued); if (rc != WOLFCERT_OK) fprintf(stderr, "enroll rc=%d (%s)\n", rc, wolfcert_strerror(rc)); REQUIRE(rc == WOLFCERT_OK); - REQUIRE(memmem(issued.data, issued.len, "BEGIN CERTIFICATE", 17) != NULL); - REQUIRE(check_subject_cn(&issued, "tls-est-client") == 0); + REQUIRE(memmem(issued.cert_pem.data, issued.cert_pem.len, + "BEGIN CERTIFICATE", 17) != NULL); + REQUIRE(check_subject_cn(&issued.cert_pem, "tls-est-client") == 0); /* The same trust anchor in DER form. */ DerBuffer* ta_der = NULL; @@ -449,7 +450,7 @@ int main(void) wolfcert_buffer_free(&ca_pem); wolfcert_buffer_free(&csr); - wolfcert_buffer_free(&issued); + wolfcert_est_result_free(&issued); wolfcert_key_free(dk); free(tls_cert); free(tls_key); diff --git a/tests/integration/test_scep_async_roundtrip.c b/tests/integration/test_scep_async_roundtrip.c index 56a453b..633c8af 100644 --- a/tests/integration/test_scep_async_roundtrip.c +++ b/tests/integration/test_scep_async_roundtrip.c @@ -287,7 +287,7 @@ static int async_enroll_path(WolfCertServer* s) REQUIRE_CLEAN(wolfcert_scep_session_open(&cli, &bsess) == WOLFCERT_OK); REQUIRE_CLEAN(wolfcert_key_generate(&kcfg2, &dk2) == WOLFCERT_OK); REQUIRE_CLEAN(wolfcert_csr_build(dk2, &meta2, &csr2) == WOLFCERT_OK); - REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req_ex(bsess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(bsess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, dk2, csr2.data, csr2.len, &rb) == WOLFCERT_OK); REQUIRE_CLEAN(rb.status == WOLFCERT_SCEP_STATUS_SUCCESS); @@ -295,7 +295,7 @@ static int async_enroll_path(WolfCertServer* s) /* RenewalReq on the same keep-alive connection. */ REQUIRE_CLEAN(wc_PemToDer(rb.cert_pem.data, (long)rb.cert_pem.len, CERT_TYPE, &rb_der, NULL, NULL, NULL) == 0); - REQUIRE_CLEAN(wolfcert_scep_session_renewal_req_ex(bsess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_renewal_req(bsess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, rb_der->buffer, rb_der->length, dk2, csr2.data, csr2.len, &rbr) == WOLFCERT_OK); @@ -304,7 +304,7 @@ static int async_enroll_path(WolfCertServer* s) /* Old cert signs a RenewalReq for a new key -> cert for the new key. */ REQUIRE_CLEAN(wolfcert_key_generate(&kcfg2, &dk3) == WOLFCERT_OK); REQUIRE_CLEAN(wolfcert_csr_build(dk3, &meta2, &csr3) == WOLFCERT_OK); - REQUIRE_CLEAN(wolfcert_scep_session_renewal_req_ex(bsess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_renewal_req(bsess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, rb_der->buffer, rb_der->length, dk2, csr3.data, csr3.len, &rnk) == WOLFCERT_OK); @@ -491,7 +491,7 @@ static int async_guard_path(WolfCertServer* s) /* Mode guard: an _ex call on an async session is rejected. */ REQUIRE_CLEAN(wolfcert_scep_session_open_async(&cli, &asess) == WOLFCERT_OK); - REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req_ex(asess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(asess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, dk, csr.data, csr.len, &rbad) == WOLFCERT_ERR_BAD_ARG); @@ -534,7 +534,7 @@ static int async_guard_path(WolfCertServer* s) REQUIRE_CLEAN(r1.fail_info == 42); /* An _ex call rejected on an async session leaves the result intact. */ - REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req_ex(asess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(asess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, dk, csr.data, csr.len, &r1) == WOLFCERT_ERR_BAD_ARG); REQUIRE_CLEAN(r1.fail_info == 42); @@ -586,13 +586,13 @@ static int blocking_poll_path(WolfCertServer* s) REQUIRE_CLEAN(wolfcert_scep_session_open(&cli, &sess) == WOLFCERT_OK); - REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req_ex(sess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, dk, csr.data, csr.len, &r1) == WOLFCERT_OK); REQUIRE_CLEAN(r1.status == WOLFCERT_SCEP_STATUS_PENDING); REQUIRE_CLEAN(r1.transaction_id != NULL && r1.transaction_id_len > 0); - REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial_ex(sess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, NULL, 0, dk, csr.data, csr.len, r1.transaction_id, r1.transaction_id_len, &r2) == WOLFCERT_OK); @@ -600,7 +600,7 @@ static int blocking_poll_path(WolfCertServer* s) REQUIRE_CLEAN(memmem(r2.cert_pem.data, r2.cert_pem.len, "BEGIN CERTIFICATE", 17) != NULL); memset(long_tid, 'A', sizeof(long_tid)); - REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial_ex(sess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, NULL, 0, dk, csr.data, csr.len, long_tid, sizeof(long_tid), &r3) == WOLFCERT_OK); @@ -608,7 +608,7 @@ static int blocking_poll_path(WolfCertServer* s) REQUIRE_CLEAN(r3.fail_info == 4); long_tid[1] = '_'; - REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial_ex(sess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, NULL, 0, dk, csr.data, csr.len, long_tid, sizeof(long_tid), &r4) == WOLFCERT_ERR_BAD_ARG); @@ -654,7 +654,7 @@ static int session_get_transport_path(WolfCertServer* s) REQUIRE_CLEAN(wolfcert_scep_session_open(&cli, &sess) == WOLFCERT_OK); - REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req_ex(sess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, dk, csr.data, csr.len, &r) == WOLFCERT_OK); REQUIRE_CLEAN(r.status == WOLFCERT_SCEP_STATUS_SUCCESS); @@ -723,14 +723,14 @@ static int non_rsa_reject_path(WolfCertServer* s) REQUIRE_CLEAN(wolfcert_key_generate(&ecfg, &ek) == WOLFCERT_OK); REQUIRE_CLEAN(wolfcert_scep_session_open(&cli, &sess) == WOLFCERT_OK); - REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req_ex(sess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, ek, csr.data, csr.len, &r) == WOLFCERT_ERR_UNSUPPORTED); - REQUIRE_CLEAN(wolfcert_scep_session_renewal_req_ex(sess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_renewal_req(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, ek, csr.data, csr.len, &r) == WOLFCERT_ERR_UNSUPPORTED); - REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial_ex(sess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, NULL, 0, ek, csr.data, csr.len, csr.data, csr.len, &r) == WOLFCERT_ERR_UNSUPPORTED); @@ -794,11 +794,11 @@ static int blocking_renewal_poll_path(WolfCertServer* s) REQUIRE_CLEAN(wolfcert_scep_session_open(&cli, &sess) == WOLFCERT_OK); /* Enroll -> PENDING, then poll (signer_cert NULL) -> issued cert. */ - REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req_ex(sess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, dk, csr.data, csr.len, &r1) == WOLFCERT_OK); REQUIRE_CLEAN(r1.status == WOLFCERT_SCEP_STATUS_PENDING); - REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial_ex(sess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, NULL, 0, dk, csr.data, csr.len, r1.transaction_id, r1.transaction_id_len, &r2) == WOLFCERT_OK); @@ -807,12 +807,12 @@ static int blocking_renewal_poll_path(WolfCertServer* s) &issued_der, NULL, NULL, NULL) == 0); /* Renew -> PENDING, then poll with the issued cert as signer_cert. */ - REQUIRE_CLEAN(wolfcert_scep_session_renewal_req_ex(sess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_renewal_req(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, issued_der->buffer, issued_der->length, dk, csr.data, csr.len, &r3) == WOLFCERT_OK); REQUIRE_CLEAN(r3.status == WOLFCERT_SCEP_STATUS_PENDING); - REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial_ex(sess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, issued_der->buffer, issued_der->length, dk, csr.data, csr.len, r3.transaction_id, r3.transaction_id_len, &r4) == WOLFCERT_OK); @@ -823,19 +823,19 @@ static int blocking_renewal_poll_path(WolfCertServer* s) * both the renewal and the poll -> SUCCESS for the new key. */ REQUIRE_CLEAN(wolfcert_key_generate(&nkcfg, &nk) == WOLFCERT_OK); REQUIRE_CLEAN(wolfcert_csr_build(nk, &nmeta, &ncsr) == WOLFCERT_OK); - REQUIRE_CLEAN(wolfcert_scep_session_renewal_req_ex(sess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_renewal_req(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, issued_der->buffer, issued_der->length, dk, ncsr.data, ncsr.len, &r5) == WOLFCERT_OK); REQUIRE_CLEAN(r5.status == WOLFCERT_SCEP_STATUS_PENDING); /* A poll signed only by the new key, as for a PKCSReq -> badCertId. */ - REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial_ex(sess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, NULL, 0, nk, ncsr.data, ncsr.len, r5.transaction_id, r5.transaction_id_len, &rx) == WOLFCERT_OK); REQUIRE_CLEAN(rx.status == WOLFCERT_SCEP_STATUS_FAILURE); REQUIRE_CLEAN(rx.fail_info == 4); - REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial_ex(sess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, issued_der->buffer, issued_der->length, dk, ncsr.data, ncsr.len, r5.transaction_id, r5.transaction_id_len, &r6) == WOLFCERT_OK); @@ -855,12 +855,12 @@ static int blocking_renewal_poll_path(WolfCertServer* s) REQUIRE_CLEAN(wolfcert_csr_build(ka, &nmeta, &csra) == WOLFCERT_OK); REQUIRE_CLEAN(wolfcert_key_generate(&nkcfg, &kb) == WOLFCERT_OK); REQUIRE_CLEAN(wolfcert_csr_build(kb, &nmeta, &csrb) == WOLFCERT_OK); - REQUIRE_CLEAN(wolfcert_scep_renewal_req_ex(&hcli, &caps, + REQUIRE_CLEAN(wolfcert_scep_renewal_req(&hcli, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, issued_der->buffer, issued_der->length, dk, csra.data, csra.len, &r7) == WOLFCERT_OK); REQUIRE_CLEAN(r7.status == WOLFCERT_SCEP_STATUS_PENDING); - REQUIRE_CLEAN(wolfcert_scep_renewal_req_ex(&hcli, &caps, + REQUIRE_CLEAN(wolfcert_scep_renewal_req(&hcli, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, issued_der->buffer, issued_der->length, dk, csra.data, csra.len, &rr) == WOLFCERT_OK); @@ -868,7 +868,7 @@ static int blocking_renewal_poll_path(WolfCertServer* s) memcmp(rr.transaction_id, r7.transaction_id, r7.transaction_id_len) == 0); REQUIRE_CLEAN(rr.status == WOLFCERT_SCEP_STATUS_PENDING); - REQUIRE_CLEAN(wolfcert_scep_renewal_req_ex(&hcli, &caps, + REQUIRE_CLEAN(wolfcert_scep_renewal_req(&hcli, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, issued_der->buffer, issued_der->length, dk, csrb.data, csrb.len, &r8) == WOLFCERT_OK); @@ -979,7 +979,7 @@ static int non_200_path(WolfCertServer* s) cli = (WolfCertServerCfg){ .protocol = WOLFCERT_PROTO_SCEP, .server_url = stub_url }; REQUIRE_CLEAN(wolfcert_scep_session_open(&cli, &sess) == WOLFCERT_OK); - REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req_ex(sess, &caps, + REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, dk, csr.data, csr.len, &r) == WOLFCERT_ERR_HTTP); @@ -1025,23 +1025,23 @@ static int negative_args_path(WolfCertServer* s) ca_len = ca_der->length; /* PKCSReq: NULL session, NULL out, NULL/zero required arg, NULL caps. */ - REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req_ex(NULL, &caps, ca, ca_len, + REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(NULL, &caps, ca, ca_len, ca, ca_len, dk, csr.data, csr.len, &r) == WOLFCERT_ERR_BAD_ARG); - REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req_ex(sess, &caps, ca, ca_len, + REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(sess, &caps, ca, ca_len, ca, ca_len, dk, csr.data, csr.len, NULL) == WOLFCERT_ERR_BAD_ARG); - REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req_ex(sess, &caps, NULL, ca_len, + REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(sess, &caps, NULL, ca_len, ca, ca_len, dk, csr.data, csr.len, &r) == WOLFCERT_ERR_BAD_ARG); - REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req_ex(sess, &caps, ca, 0, + REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(sess, &caps, ca, 0, ca, ca_len, dk, csr.data, csr.len, &r) == WOLFCERT_ERR_BAD_ARG); - REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req_ex(sess, NULL, ca, ca_len, + REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(sess, NULL, ca, ca_len, ca, ca_len, dk, csr.data, csr.len, &r) == WOLFCERT_ERR_BAD_ARG); /* RenewalReq: a NULL op-specific required arg (current_cert). */ - REQUIRE_CLEAN(wolfcert_scep_session_renewal_req_ex(sess, &caps, ca, ca_len, + REQUIRE_CLEAN(wolfcert_scep_session_renewal_req(sess, &caps, ca, ca_len, ca, ca_len, NULL, 0, dk, csr.data, csr.len, &r) == WOLFCERT_ERR_BAD_ARG); /* GetCertInitial: a NULL transactionID. */ - REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial_ex(sess, &caps, ca, ca_len, + REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial(sess, &caps, ca, ca_len, ca, ca_len, NULL, 0, dk, csr.data, csr.len, NULL, 0, &r) == WOLFCERT_ERR_BAD_ARG); diff --git a/tests/integration/test_scep_get_cert.c b/tests/integration/test_scep_get_cert.c index 8421ff3..906da29 100644 --- a/tests/integration/test_scep_get_cert.c +++ b/tests/integration/test_scep_get_cert.c @@ -78,10 +78,10 @@ static int get_cert_path(WolfCertServer* s) /* Step 1: enroll, so the CA has a certificate to hand back. */ WolfCertScepResult r1 = { 0 }; - REQUIRE(wolfcert_scep_pkcs_req_ex(&cli, &caps, - ca_der->buffer, ca_der->length, - ca_der->buffer, ca_der->length, - dk, csr.data, csr.len, &r1) == WOLFCERT_OK); + REQUIRE(wolfcert_scep_pkcs_req(&cli, &caps, + ca_der->buffer, ca_der->length, + ca_der->buffer, ca_der->length, + dk, csr.data, csr.len, &r1) == WOLFCERT_OK); REQUIRE(r1.status == WOLFCERT_SCEP_STATUS_SUCCESS); REQUIRE(r1.cert_pem.data != NULL); @@ -166,11 +166,11 @@ static int get_cert_path(WolfCertServer* s) REQUIRE(wolfcert_csr_build(other, &other_meta, &other_csr) == WOLFCERT_OK); WolfCertScepResult ro = { 0 }; - REQUIRE(wolfcert_scep_pkcs_req_ex(&cli, &caps, - ca_der->buffer, ca_der->length, - ca_der->buffer, ca_der->length, - other, other_csr.data, other_csr.len, - &ro) == WOLFCERT_OK); + REQUIRE(wolfcert_scep_pkcs_req(&cli, &caps, + ca_der->buffer, ca_der->length, + ca_der->buffer, ca_der->length, + other, other_csr.data, other_csr.len, + &ro) == WOLFCERT_OK); REQUIRE(ro.status == WOLFCERT_SCEP_STATUS_SUCCESS); DerBuffer* other_der = NULL; @@ -211,10 +211,10 @@ static int get_cert_path(WolfCertServer* s) size_t last_serial_len = 0; for (int i = 0; i < 17; i++) { WolfCertScepResult rn = { 0 }; - REQUIRE(wolfcert_scep_pkcs_req_ex(&cli, &caps, - ca_der->buffer, ca_der->length, - ca_der->buffer, ca_der->length, - dk, csr.data, csr.len, &rn) == WOLFCERT_OK); + REQUIRE(wolfcert_scep_pkcs_req(&cli, &caps, + ca_der->buffer, ca_der->length, + ca_der->buffer, ca_der->length, + dk, csr.data, csr.len, &rn) == WOLFCERT_OK); REQUIRE(rn.status == WOLFCERT_SCEP_STATUS_SUCCESS); DerBuffer* nd = NULL; @@ -331,10 +331,10 @@ static int disabled_path(WolfCertServer* s) REQUIRE(wolfcert_csr_build(dk, &meta, &csr) == WOLFCERT_OK); WolfCertScepResult r1 = { 0 }; - REQUIRE(wolfcert_scep_pkcs_req_ex(&cli, &caps, - ca_der->buffer, ca_der->length, - ca_der->buffer, ca_der->length, - dk, csr.data, csr.len, &r1) == WOLFCERT_OK); + REQUIRE(wolfcert_scep_pkcs_req(&cli, &caps, + ca_der->buffer, ca_der->length, + ca_der->buffer, ca_der->length, + dk, csr.data, csr.len, &r1) == WOLFCERT_OK); REQUIRE(r1.status == WOLFCERT_SCEP_STATUS_SUCCESS); DerBuffer* issued_der = NULL; diff --git a/tests/integration/test_scep_poll_roundtrip.c b/tests/integration/test_scep_poll_roundtrip.c index 3323ae3..6ad27e2 100644 --- a/tests/integration/test_scep_poll_roundtrip.c +++ b/tests/integration/test_scep_poll_roundtrip.c @@ -192,24 +192,16 @@ static int poll_path(WolfCertServer* s) /* Step 1: PKCSReq - must return PENDING with a transactionID. */ WolfCertScepResult r1 = { 0 }; - int rc = wolfcert_scep_pkcs_req_ex(&cli, &caps, - ca_der->buffer, ca_der->length, - ca_der->buffer, ca_der->length, - dk, csr.data, csr.len, &r1); + int rc = wolfcert_scep_pkcs_req(&cli, &caps, + ca_der->buffer, ca_der->length, + ca_der->buffer, ca_der->length, + dk, csr.data, csr.len, &r1); REQUIRE(rc == WOLFCERT_OK); REQUIRE(r1.status == WOLFCERT_SCEP_STATUS_PENDING); REQUIRE(r1.transaction_id != NULL); REQUIRE(r1.transaction_id_len > 0); REQUIRE(r1.cert_pem.data == NULL); - /* The legacy API maps PENDING -> WOLFCERT_ERR_PENDING. */ - WolfCertBuffer legacy_out = { 0 }; - int legacy_rc = wolfcert_scep_pkcs_req(&cli, &caps, - ca_der->buffer, ca_der->length, - dk, csr.data, csr.len, &legacy_out); - REQUIRE(legacy_rc == WOLFCERT_ERR_PENDING); - wolfcert_buffer_free(&legacy_out); - /* A poll with absent or non-envelope content is refused, entry kept. */ static const uint8_t not_env[] = { 0x04, 0x03, 'a', 'b', 'c' }; const uint8_t* bad_content[2] = { NULL, not_env }; @@ -296,10 +288,10 @@ static int poll_path(WolfCertServer* s) /* A CSR with a broken signature is refused at PKCSReq and never parked. */ other_csr.data[other_csr.len - 1] ^= 0x01; - rc = wolfcert_scep_pkcs_req_ex(&cli, &caps, - ca_der->buffer, ca_der->length, - ca_der->buffer, ca_der->length, - other, other_csr.data, other_csr.len, &rx); + rc = wolfcert_scep_pkcs_req(&cli, &caps, + ca_der->buffer, ca_der->length, + ca_der->buffer, ca_der->length, + other, other_csr.data, other_csr.len, &rx); REQUIRE(rc == WOLFCERT_OK); REQUIRE(rx.status == WOLFCERT_SCEP_STATUS_FAILURE); REQUIRE(rx.fail_info == 2); @@ -371,17 +363,17 @@ static int poll_path(WolfCertServer* s) WolfCertScepResult r8 = { 0 }; WolfCertScepResult r9 = { 0 }; wolfcert_scep_server_set_oom_fault(s, 3); - rc = wolfcert_scep_pkcs_req_ex(&cli, &caps, - ca_der->buffer, ca_der->length, - ca_der->buffer, ca_der->length, - dk, csr.data, csr.len, &r7); + rc = wolfcert_scep_pkcs_req(&cli, &caps, + ca_der->buffer, ca_der->length, + ca_der->buffer, ca_der->length, + dk, csr.data, csr.len, &r7); wolfcert_scep_server_set_oom_fault(s, 0); REQUIRE(rc != WOLFCERT_OK); wolfcert_scep_result_free(&r7); - rc = wolfcert_scep_pkcs_req_ex(&cli, &caps, - ca_der->buffer, ca_der->length, - ca_der->buffer, ca_der->length, - dk, csr.data, csr.len, &r7); + rc = wolfcert_scep_pkcs_req(&cli, &caps, + ca_der->buffer, ca_der->length, + ca_der->buffer, ca_der->length, + dk, csr.data, csr.len, &r7); REQUIRE(rc == WOLFCERT_OK); REQUIRE(r7.status == WOLFCERT_SCEP_STATUS_PENDING); wolfcert_scep_server_set_oom_fault(s, 2); @@ -414,10 +406,10 @@ static int poll_path(WolfCertServer* s) int dir_csr_len = dir_san_csr(dk, dir_csr, (int)sizeof(dir_csr)); WolfCertScepResult rd = { 0 }; REQUIRE(dir_csr_len > 0); - rc = wolfcert_scep_pkcs_req_ex(&cli, &caps, - ca_der->buffer, ca_der->length, - ca_der->buffer, ca_der->length, - dk, dir_csr, (size_t)dir_csr_len, &rd); + rc = wolfcert_scep_pkcs_req(&cli, &caps, + ca_der->buffer, ca_der->length, + ca_der->buffer, ca_der->length, + dk, dir_csr, (size_t)dir_csr_len, &rd); REQUIRE(rc == WOLFCERT_OK); REQUIRE(rd.status == WOLFCERT_SCEP_STATUS_PENDING); for (int i = 0; i < 2; i++) { @@ -536,10 +528,10 @@ static int empty_subject_path(WolfCertServer* s) REQUIRE(wolfcert_csr_build(dk, &meta, &csr) == WOLFCERT_OK); WolfCertScepResult r1 = { 0 }; - REQUIRE(wolfcert_scep_pkcs_req_ex(&cli, &caps, - ca_der->buffer, ca_der->length, - ca_der->buffer, ca_der->length, - dk, csr.data, csr.len, &r1) + REQUIRE(wolfcert_scep_pkcs_req(&cli, &caps, + ca_der->buffer, ca_der->length, + ca_der->buffer, ca_der->length, + dk, csr.data, csr.len, &r1) == WOLFCERT_OK); REQUIRE(r1.status == WOLFCERT_SCEP_STATUS_PENDING); diff --git a/tests/integration/test_scep_roundtrip.c b/tests/integration/test_scep_roundtrip.c index 85098fa..56031e8 100644 --- a/tests/integration/test_scep_roundtrip.c +++ b/tests/integration/test_scep_roundtrip.c @@ -269,7 +269,7 @@ static int check_get_fallback(const WolfCertServerCfg* cli, WolfCertCertMeta meta_get = { .subject_dn = "CN=device-scep-get" }; WolfCertKey* dkg = NULL; WolfCertBuffer csr_get = { 0 }; - WolfCertBuffer issued_get = { 0 }; + WolfCertScepResult issued_get = { 0 }; WOLFSSL_CERT_MANAGER* cm = NULL; DerBuffer* issued_der = NULL; int rc; @@ -281,7 +281,11 @@ static int check_get_fallback(const WolfCertServerCfg* cli, rc = wolfcert_csr_build(dkg, &meta_get, &csr_get); if (rc == WOLFCERT_OK) rc = wolfcert_scep_pkcs_req(cli, &caps_get, ca_der_buf, ca_der_len, + ca_der_buf, ca_der_len, dkg, csr_get.data, csr_get.len, &issued_get); + if (rc == WOLFCERT_OK && + issued_get.status != WOLFCERT_SCEP_STATUS_SUCCESS) + rc = -1; if (rc == WOLFCERT_OK) { cm = wolfSSL_CertManagerNew(); @@ -293,7 +297,8 @@ static int check_get_fallback(const WolfCertServerCfg* cli, WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) rc = -1; if (rc == WOLFCERT_OK && - wc_PemToDer(issued_get.data, (long)issued_get.len, CERT_TYPE, + wc_PemToDer(issued_get.cert_pem.data, + (long)issued_get.cert_pem.len, CERT_TYPE, &issued_der, NULL, NULL, NULL) != 0) rc = -1; if (rc == WOLFCERT_OK && @@ -305,7 +310,7 @@ static int check_get_fallback(const WolfCertServerCfg* cli, wc_FreeDer(&issued_der); if (cm != NULL) wolfSSL_CertManagerFree(cm); - wolfcert_buffer_free(&issued_get); + wolfcert_scep_result_free(&issued_get); wolfcert_buffer_free(&csr_get); wolfcert_key_free(dkg); return rc; @@ -320,7 +325,7 @@ static int check_rsa4096(const WolfCertServerCfg* cli, WolfCertCertMeta meta = { .subject_dn = "CN=device-scep-4096" }; WolfCertKey* key = NULL; WolfCertBuffer csr = { 0 }; - WolfCertBuffer issued = { 0 }; + WolfCertScepResult issued = { 0 }; WOLFSSL_CERT_MANAGER* cm = NULL; DerBuffer* issued_der = NULL; int rc; @@ -330,7 +335,10 @@ static int check_rsa4096(const WolfCertServerCfg* cli, rc = wolfcert_csr_build(key, &meta, &csr); if (rc == WOLFCERT_OK) rc = wolfcert_scep_pkcs_req(cli, caps, ca_der_buf, ca_der_len, + ca_der_buf, ca_der_len, key, csr.data, csr.len, &issued); + if (rc == WOLFCERT_OK && issued.status != WOLFCERT_SCEP_STATUS_SUCCESS) + rc = -1; if (rc == WOLFCERT_OK) { cm = wolfSSL_CertManagerNew(); @@ -342,7 +350,8 @@ static int check_rsa4096(const WolfCertServerCfg* cli, WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) rc = -1; if (rc == WOLFCERT_OK && - wc_PemToDer(issued.data, (long)issued.len, CERT_TYPE, + wc_PemToDer(issued.cert_pem.data, (long)issued.cert_pem.len, + CERT_TYPE, &issued_der, NULL, NULL, NULL) != 0) rc = -1; if (rc == WOLFCERT_OK && @@ -354,7 +363,7 @@ static int check_rsa4096(const WolfCertServerCfg* cli, wc_FreeDer(&issued_der); if (cm != NULL) wolfSSL_CertManagerFree(cm); - wolfcert_buffer_free(&issued); + wolfcert_scep_result_free(&issued); wolfcert_buffer_free(&csr); wolfcert_key_free(key); return rc; @@ -380,9 +389,9 @@ static int check_pubkey_txid(const WolfCertServerCfg* cli, if (rc == WOLFCERT_OK) rc = wolfcert_csr_build(key, &meta, &csr); if (rc == WOLFCERT_OK) - rc = wolfcert_scep_pkcs_req_ex(&cli_ph, caps, ca_der_buf, ca_der_len, - ca_der_buf, ca_der_len, key, - csr.data, csr.len, &r1); + rc = wolfcert_scep_pkcs_req(&cli_ph, caps, ca_der_buf, ca_der_len, + ca_der_buf, ca_der_len, key, + csr.data, csr.len, &r1); if (rc == WOLFCERT_OK && r1.status != WOLFCERT_SCEP_STATUS_SUCCESS) rc = -1; if (rc == WOLFCERT_OK && r1.transaction_id_len != 64) /* SHA-256 hex */ @@ -417,9 +426,9 @@ static int check_pubkey_txid(const WolfCertServerCfg* cli, /* Deterministic: enrolling the same key again reuses the transactionID. */ if (rc == WOLFCERT_OK) - rc = wolfcert_scep_pkcs_req_ex(&cli_ph, caps, ca_der_buf, ca_der_len, - ca_der_buf, ca_der_len, key, - csr.data, csr.len, &r2); + rc = wolfcert_scep_pkcs_req(&cli_ph, caps, ca_der_buf, ca_der_len, + ca_der_buf, ca_der_len, key, + csr.data, csr.len, &r2); if (rc == WOLFCERT_OK && (r2.transaction_id_len != r1.transaction_id_len || memcmp(r1.transaction_id, r2.transaction_id, r1.transaction_id_len) != 0)) @@ -450,9 +459,9 @@ static int check_bad_csr_sig(const WolfCertServerCfg* cli, if (rc == WOLFCERT_OK) { memcpy(bad, csr, csr_len); bad[csr_len - 1] ^= 0x01; - rc = wolfcert_scep_pkcs_req_ex(cli, caps, ca_der_buf, ca_der_len, - ca_der_buf, ca_der_len, key, - bad, csr_len, &r); + rc = wolfcert_scep_pkcs_req(cli, caps, ca_der_buf, ca_der_len, + ca_der_buf, ca_der_len, key, + bad, csr_len, &r); if (rc != WOLFCERT_OK) fprintf(stderr, "bad CSR signature: rc=%d (%s)\n", rc, wolfcert_strerror(rc)); @@ -482,10 +491,10 @@ static int check_renewal_foreign_signer(const WolfCertServerCfg* cli, rc = wolfcert_scep_self_signed_rsa((RsaKey*)key->impl, csr, csr_len, &signer, &signer_len, NULL); if (rc == WOLFCERT_OK) { - rc = wolfcert_scep_renewal_req_ex(cli, caps, ca_der_buf, ca_der_len, - ca_der_buf, ca_der_len, - signer, signer_len, key, - csr, csr_len, &r); + rc = wolfcert_scep_renewal_req(cli, caps, ca_der_buf, ca_der_len, + ca_der_buf, ca_der_len, + signer, signer_len, key, + csr, csr_len, &r); if (rc != WOLFCERT_OK) fprintf(stderr, "foreign renewal signer: rc=%d (%s)\n", rc, wolfcert_strerror(rc)); @@ -508,23 +517,26 @@ static int check_signing_hash(const WolfCertServerCfg* cli, { WolfCertServerCfg c = *cli; WolfCertScepCaps hc = *caps; - WolfCertBuffer issued = { 0 }; + WolfCertScepResult issued = { 0 }; int rc; hc.sha512 = sha512; hc.sha384 = sha384; c.proto_opts.scep.signing_hash = hash; - rc = wolfcert_scep_pkcs_req(&c, &hc, ca_der_buf, ca_der_len, key, + rc = wolfcert_scep_pkcs_req(&c, &hc, ca_der_buf, ca_der_len, + ca_der_buf, ca_der_len, key, csr->data, csr->len, &issued); if (rc == WOLFCERT_OK && - memmem(issued.data, issued.len, "BEGIN CERTIFICATE", 17) == NULL) + (issued.status != WOLFCERT_SCEP_STATUS_SUCCESS || + memmem(issued.cert_pem.data, issued.cert_pem.len, + "BEGIN CERTIFICATE", 17) == NULL)) rc = -1; if (rc != WOLFCERT_OK) fprintf(stderr, "signing hash %d (caps 512=%d 384=%d): rc=%d\n", (int)hash, sha512, sha384, rc); - wolfcert_buffer_free(&issued); + wolfcert_scep_result_free(&issued); return rc; } @@ -538,7 +550,8 @@ static int check_content_cipher(const WolfCertServerCfg* cli, WolfCertServerCfg c = *cli; WolfCertCertMeta meta = { .subject_dn = "CN=device-cipher" }; WolfCertKey* key = NULL; - WolfCertBuffer csr = { 0 }, issued = { 0 }; + WolfCertBuffer csr = { 0 }; + WolfCertScepResult issued = { 0 }; int rc; c.proto_opts.scep.content_cipher = cipher; @@ -547,13 +560,16 @@ static int check_content_cipher(const WolfCertServerCfg* cli, if (rc == WOLFCERT_OK) rc = wolfcert_csr_build(key, &meta, &csr); if (rc == WOLFCERT_OK) - rc = wolfcert_scep_pkcs_req(&c, caps, ca_der_buf, ca_der_len, key, + rc = wolfcert_scep_pkcs_req(&c, caps, ca_der_buf, ca_der_len, + ca_der_buf, ca_der_len, key, csr.data, csr.len, &issued); if (rc == WOLFCERT_OK && - memmem(issued.data, issued.len, "BEGIN CERTIFICATE", 17) == NULL) + (issued.status != WOLFCERT_SCEP_STATUS_SUCCESS || + memmem(issued.cert_pem.data, issued.cert_pem.len, + "BEGIN CERTIFICATE", 17) == NULL)) rc = -1; - wolfcert_buffer_free(&issued); + wolfcert_scep_result_free(&issued); wolfcert_buffer_free(&csr); wolfcert_key_free(key); return rc; @@ -1001,14 +1017,16 @@ static int check_content_cipher_wire(const WolfCertScepCaps* caps, WolfCertCertMeta meta = { .subject_dn = "CN=device-cipher-wire" }; WolfCertKey* key = NULL; - WolfCertBuffer csr = { 0 }, issued = { 0 }; + WolfCertBuffer csr = { 0 }; + WolfCertScepResult issued = { 0 }; REQUIRE(wolfcert_key_generate(kcfg, &key) == WOLFCERT_OK); REQUIRE(wolfcert_csr_build(key, &meta, &csr) == WOLFCERT_OK); /* The listener never answers, so the call fails. */ - (void)wolfcert_scep_pkcs_req(&cli, caps, ca_der_buf, ca_der_len, key, + (void)wolfcert_scep_pkcs_req(&cli, caps, ca_der_buf, ca_der_len, + ca_der_buf, ca_der_len, key, csr.data, csr.len, &issued); - wolfcert_buffer_free(&issued); + wolfcert_scep_result_free(&issued); wolfcert_buffer_free(&csr); wolfcert_key_free(key); pthread_join(tid, NULL); @@ -1038,11 +1056,12 @@ static int check_signing_hash_wire(const WolfCertScepCaps* caps, int sha512, hc.sha512 = sha512; hc.sha384 = sha384; - WolfCertBuffer issued = { 0 }; + WolfCertScepResult issued = { 0 }; /* The listener never answers, so the call fails. */ - (void)wolfcert_scep_pkcs_req(&cli, &hc, ca_der_buf, ca_der_len, key, + (void)wolfcert_scep_pkcs_req(&cli, &hc, ca_der_buf, ca_der_len, + ca_der_buf, ca_der_len, key, csr->data, csr->len, &issued); - wolfcert_buffer_free(&issued); + wolfcert_scep_result_free(&issued); pthread_join(tid, NULL); if (strcmp(mc.digest, expect) != 0) @@ -1074,10 +1093,10 @@ static int check_renewal_msg_type(const WolfCertScepCaps* caps, WolfCertScepResult r = { 0 }; /* The capture server never answers, so the call fails. */ - (void)wolfcert_scep_renewal_req_ex(&cli, caps, ca_der_buf, ca_der_len, - ca_der_buf, ca_der_len, - cur_cert, cur_cert_len, cur_key, - csr, csr_len, &r); + (void)wolfcert_scep_renewal_req(&cli, caps, ca_der_buf, ca_der_len, + ca_der_buf, ca_der_len, + cur_cert, cur_cert_len, cur_key, + csr, csr_len, &r); wolfcert_scep_result_free(&r); pthread_join(tid, NULL); @@ -1114,9 +1133,9 @@ static int check_pki_status(const WolfCertScepCaps* caps, WolfCertServerCfg cli = { .protocol = WOLFCERT_PROTO_SCEP, .server_url = url }; WolfCertScepResult r = { 0 }; - int rc = wolfcert_scep_pkcs_req_ex(&cli, caps, signer_cert, signer_cert_len, - signer_cert, signer_cert_len, key, - csr, csr_len, &r); + int rc = wolfcert_scep_pkcs_req(&cli, caps, signer_cert, signer_cert_len, + signer_cert, signer_cert_len, key, + csr, csr_len, &r); WolfCertScepStatus got = r.status; int got_tid = r.transaction_id != NULL || r.transaction_id_len != 0; int got_cert = r.cert_pem.data != NULL || r.cert_pem.len != 0; @@ -1165,10 +1184,10 @@ static int check_session_opts_capture(const WolfCertScepCaps* caps, WolfCertScepResult r = { 0 }; /* The listener never answers, so this fails. */ - (void)wolfcert_scep_session_renewal_req_ex(sess, &hc, ca_der_buf, ca_der_len, - ca_der_buf, ca_der_len, - cur_cert, cur_cert_len, cur_key, - csr, csr_len, &r); + (void)wolfcert_scep_session_renewal_req(sess, &hc, ca_der_buf, ca_der_len, + ca_der_buf, ca_der_len, + cur_cert, cur_cert_len, cur_key, + csr, csr_len, &r); wolfcert_scep_result_free(&r); wolfcert_scep_session_close(sess); pthread_join(tid, NULL); @@ -1859,13 +1878,13 @@ static int test_split_ra(void) rc = wolfcert_csr_build(key, &meta, &csr); if (rc == WOLFCERT_OK) { - via_ra_rc = wolfcert_scep_pkcs_req_ex(&cli, &caps, ra->buffer, + via_ra_rc = wolfcert_scep_pkcs_req(&cli, &caps, ra->buffer, ra->length, bundle.data, bundle.len, key, csr.data, csr.len, &via_ra); - ca_trust_rc = wolfcert_scep_pkcs_req_ex(&cli, &caps, ra->buffer, + ca_trust_rc = wolfcert_scep_pkcs_req(&cli, &caps, ra->buffer, ra->length, ca->buffer, ca->length, key, csr.data, csr.len, &ca_trust); - to_ca_rc = wolfcert_scep_pkcs_req_ex(&cli, &caps, ca->buffer, + to_ca_rc = wolfcert_scep_pkcs_req(&cli, &caps, ca->buffer, ca->length, bundle.data, bundle.len, key, csr.data, csr.len, &to_ca); } @@ -1989,21 +2008,24 @@ int main(void) WolfCertBuffer csr = { 0 }; REQUIRE(wolfcert_csr_build(dk, &meta, &csr) == WOLFCERT_OK); - WolfCertBuffer issued = { 0 }; + WolfCertScepResult issued = { 0 }; int rc = wolfcert_scep_pkcs_req(&cli, &caps, ca_der->buffer, ca_der->length, + ca_der->buffer, ca_der->length, dk, csr.data, csr.len, &issued); if (rc != WOLFCERT_OK) fprintf(stderr, "SCEP rc=%d (%s)\n", rc, wolfcert_strerror(rc)); REQUIRE(rc == WOLFCERT_OK); - REQUIRE(memmem(issued.data, issued.len, "BEGIN CERTIFICATE", 17) != NULL); + REQUIRE(issued.status == WOLFCERT_SCEP_STATUS_SUCCESS); + REQUIRE(memmem(issued.cert_pem.data, issued.cert_pem.len, + "BEGIN CERTIFICATE", 17) != NULL); WOLFSSL_CERT_MANAGER* cm = wolfSSL_CertManagerNew(); REQUIRE(cm != NULL); REQUIRE(wolfSSL_CertManagerLoadCABuffer(cm, ca_pem.data, (long)ca_pem.len, WOLFSSL_FILETYPE_PEM) == WOLFSSL_SUCCESS); DerBuffer* issued_der = NULL; - REQUIRE(wc_PemToDer(issued.data, (long)issued.len, CERT_TYPE, - &issued_der, NULL, NULL, NULL) == 0); + REQUIRE(wc_PemToDer(issued.cert_pem.data, (long)issued.cert_pem.len, + CERT_TYPE, &issued_der, NULL, NULL, NULL) == 0); REQUIRE(wolfSSL_CertManagerVerifyBuffer(cm, issued_der->buffer, (long)issued_der->length, WOLFSSL_FILETYPE_ASN1) == WOLFSSL_SUCCESS); @@ -2211,10 +2233,12 @@ int main(void) WolfCertCertMeta edmeta = { .subject_dn = "CN=ed-scep" }; WolfCertBuffer edcsr = { 0 }; REQUIRE(wolfcert_csr_build(edk, &edmeta, &edcsr) == WOLFCERT_OK); - WolfCertBuffer edout = { 0 }; + WolfCertScepResult edout = { 0 }; REQUIRE(wolfcert_scep_pkcs_req(&cli, &caps, ca_der->buffer, ca_der->length, + ca_der->buffer, ca_der->length, edk, edcsr.data, edcsr.len, &edout) == WOLFCERT_ERR_UNSUPPORTED); + wolfcert_scep_result_free(&edout); wolfcert_buffer_free(&edcsr); wolfcert_key_free(edk); #endif @@ -2248,15 +2272,18 @@ int main(void) WolfCertKey* dk2 = NULL; REQUIRE(wolfcert_key_generate(&kcfg, &dk2) == WOLFCERT_OK); - /* 1) no challenge -> signed CertRep FAILURE, surfaced as ERR_PROTOCOL; - * a bare HTTP 4xx would read back as ERR_HTTP. */ + /* 1) no challenge -> signed CertRep FAILURE; a bare HTTP 4xx would read + * back as ERR_HTTP. */ WolfCertCertMeta meta_none = { .subject_dn = "CN=chal-none" }; WolfCertBuffer csr_none = { 0 }; REQUIRE(wolfcert_csr_build(dk2, &meta_none, &csr_none) == WOLFCERT_OK); - WolfCertBuffer out_none = { 0 }; + WolfCertScepResult out_none = { 0 }; REQUIRE(wolfcert_scep_pkcs_req(&cli2, &caps2, ca2_der->buffer, ca2_der->length, + ca2_der->buffer, ca2_der->length, dk2, csr_none.data, csr_none.len, &out_none) - == WOLFCERT_ERR_PROTOCOL); + == WOLFCERT_OK); + REQUIRE(out_none.status == WOLFCERT_SCEP_STATUS_FAILURE); + wolfcert_scep_result_free(&out_none); wolfcert_buffer_free(&csr_none); /* 2) wrong challenge -> same CertRep FAILURE path */ @@ -2264,10 +2291,13 @@ int main(void) .challenge_password = "battery-staple" }; WolfCertBuffer csr_bad = { 0 }; REQUIRE(wolfcert_csr_build(dk2, &meta_bad, &csr_bad) == WOLFCERT_OK); - WolfCertBuffer out_bad = { 0 }; + WolfCertScepResult out_bad = { 0 }; REQUIRE(wolfcert_scep_pkcs_req(&cli2, &caps2, ca2_der->buffer, ca2_der->length, + ca2_der->buffer, ca2_der->length, dk2, csr_bad.data, csr_bad.len, &out_bad) - == WOLFCERT_ERR_PROTOCOL); + == WOLFCERT_OK); + REQUIRE(out_bad.status == WOLFCERT_SCEP_STATUS_FAILURE); + wolfcert_scep_result_free(&out_bad); wolfcert_buffer_free(&csr_bad); /* 3) correct challenge -> issuance succeeds */ @@ -2275,13 +2305,16 @@ int main(void) .challenge_password = "correct-horse" }; WolfCertBuffer csr_ok = { 0 }; REQUIRE(wolfcert_csr_build(dk2, &meta_ok, &csr_ok) == WOLFCERT_OK); - WolfCertBuffer out_ok = { 0 }; + WolfCertScepResult out_ok = { 0 }; REQUIRE(wolfcert_scep_pkcs_req(&cli2, &caps2, ca2_der->buffer, ca2_der->length, + ca2_der->buffer, ca2_der->length, dk2, csr_ok.data, csr_ok.len, &out_ok) == WOLFCERT_OK); - REQUIRE(memmem(out_ok.data, out_ok.len, "BEGIN CERTIFICATE", 17) != NULL); + REQUIRE(out_ok.status == WOLFCERT_SCEP_STATUS_SUCCESS); + REQUIRE(memmem(out_ok.cert_pem.data, out_ok.cert_pem.len, + "BEGIN CERTIFICATE", 17) != NULL); wolfcert_buffer_free(&csr_ok); - wolfcert_buffer_free(&out_ok); + wolfcert_scep_result_free(&out_ok); wolfcert_server_stop(s2); pthread_join(tid2, NULL); @@ -2374,8 +2407,9 @@ int main(void) WolfCertCertMeta meta3 = { .subject_dn = "CN=scep-no-rnonce" }; WolfCertBuffer csr3 = { 0 }; REQUIRE(wolfcert_csr_build(dk3, &meta3, &csr3) == WOLFCERT_OK); - WolfCertBuffer out3 = { 0 }; + WolfCertScepResult out3 = { 0 }; REQUIRE(wolfcert_scep_pkcs_req(&cli3, &caps3, ca3_der->buffer, ca3_der->length, + ca3_der->buffer, ca3_der->length, dk3, csr3.data, csr3.len, &out3) == WOLFCERT_ERR_PROTOCOL); @@ -2385,7 +2419,7 @@ int main(void) wc_FreeDer(&ca3_der); wolfcert_buffer_free(&ca3_pem); wolfcert_buffer_free(&csr3); - wolfcert_buffer_free(&out3); + wolfcert_scep_result_free(&out3); wolfcert_key_free(dk3); /* A CertRep signed by a key other than the CA's is rejected. */ @@ -2415,8 +2449,9 @@ int main(void) WolfCertCertMeta meta4 = { .subject_dn = "CN=scep-wrong-signer" }; WolfCertBuffer csr4 = { 0 }; REQUIRE(wolfcert_csr_build(dk4, &meta4, &csr4) == WOLFCERT_OK); - WolfCertBuffer out4 = { 0 }; + WolfCertScepResult out4 = { 0 }; REQUIRE(wolfcert_scep_pkcs_req(&cli4, &caps4, ca4_der->buffer, ca4_der->length, + ca4_der->buffer, ca4_der->length, dk4, csr4.data, csr4.len, &out4) == WOLFCERT_ERR_AUTH); @@ -2426,7 +2461,7 @@ int main(void) wc_FreeDer(&ca4_der); wolfcert_buffer_free(&ca4_pem); wolfcert_buffer_free(&csr4); - wolfcert_buffer_free(&out4); + wolfcert_scep_result_free(&out4); wolfcert_key_free(dk4); /* A failed senderNonce RNG draw on the server answers HTTP 500. */ @@ -2456,8 +2491,9 @@ int main(void) WolfCertCertMeta meta5 = { .subject_dn = "CN=scep-rng-fail" }; WolfCertBuffer csr5 = { 0 }; REQUIRE(wolfcert_csr_build(dk5, &meta5, &csr5) == WOLFCERT_OK); - WolfCertBuffer out5 = { 0 }; + WolfCertScepResult out5 = { 0 }; REQUIRE(wolfcert_scep_pkcs_req(&cli5, &caps5, ca5_der->buffer, ca5_der->length, + ca5_der->buffer, ca5_der->length, dk5, csr5.data, csr5.len, &out5) == WOLFCERT_ERR_HTTP); @@ -2467,14 +2503,14 @@ int main(void) wc_FreeDer(&ca5_der); wolfcert_buffer_free(&ca5_pem); wolfcert_buffer_free(&csr5); - wolfcert_buffer_free(&out5); + wolfcert_scep_result_free(&out5); wolfcert_key_free(dk5); wc_FreeDer(&ca_der); wc_FreeDer(&issued_der); wolfcert_buffer_free(&ca_pem); wolfcert_buffer_free(&csr); - wolfcert_buffer_free(&issued); + wolfcert_scep_result_free(&issued); wolfcert_key_free(dk); test_ca_store_close(); wolfcert_cleanup(); diff --git a/tests/unit/test_est.c b/tests/unit/test_est.c index 8a421e9..6a88139 100644 --- a/tests/unit/test_est.c +++ b/tests/unit/test_est.c @@ -288,6 +288,7 @@ static int test_est_require_server_auth(void) .verify_server = 0 }; WolfCertBuffer out = { 0 }; + WolfCertEstResult res = { 0 }; WolfCertEstSession* sess = NULL; WolfCertKeyCfg kcfg = { .type = TEST_ENROLL_KEY_TYPE, .param = TEST_ENROLL_KEY_PARAM, .dev_id = WOLFCERT_DEVID_SOFTWARE }; @@ -297,12 +298,12 @@ static int test_est_require_server_auth(void) REQUIRE(wolfcert_est_get_cacerts(&srv, &out) == WOLFCERT_ERR_TLS); REQUIRE(wolfcert_est_get_csr_attrs(&srv, &out) == WOLFCERT_ERR_TLS); REQUIRE(wolfcert_est_simple_enroll(&srv, dummy_csr, sizeof(dummy_csr), - &out) == WOLFCERT_ERR_TLS); + &res) == WOLFCERT_ERR_TLS); /* Reenroll with a generated key so verify_server = 0 is what fails it. */ REQUIRE(wolfcert_key_generate(&kcfg, &rk) == WOLFCERT_OK); REQUIRE(wolfcert_est_simple_reenroll(&srv, dummy_csr, sizeof(dummy_csr), rk, - dummy_csr, sizeof(dummy_csr), &out) + dummy_csr, sizeof(dummy_csr), &res) == WOLFCERT_ERR_TLS); wolfcert_key_free(rk); @@ -322,7 +323,7 @@ static int test_est_require_server_auth(void) /* Bad arguments fail before the dial. */ REQUIRE(wolfcert_est_get_cacerts(&srv, NULL) == WOLFCERT_ERR_BAD_ARG); - REQUIRE(wolfcert_est_simple_enroll(&srv, NULL, 0, &out) + REQUIRE(wolfcert_est_simple_enroll(&srv, NULL, 0, &res) == WOLFCERT_ERR_BAD_ARG); return 0; @@ -343,6 +344,7 @@ static int test_est_rejects_scep_cfg(void) } }; WolfCertBuffer out = { 0 }; + WolfCertEstResult res = { 0 }; WolfCertEstSession* sess = NULL; WolfCertKeyCfg kcfg = { .type = TEST_ENROLL_KEY_TYPE, .param = TEST_ENROLL_KEY_PARAM, .dev_id = WOLFCERT_DEVID_SOFTWARE }; @@ -351,11 +353,11 @@ static int test_est_rejects_scep_cfg(void) REQUIRE(wolfcert_est_get_cacerts(&srv, &out) == WOLFCERT_ERR_BAD_ARG); REQUIRE(wolfcert_est_get_csr_attrs(&srv, &out) == WOLFCERT_ERR_BAD_ARG); REQUIRE(wolfcert_est_simple_enroll(&srv, dummy_csr, sizeof(dummy_csr), - &out) == WOLFCERT_ERR_BAD_ARG); + &res) == WOLFCERT_ERR_BAD_ARG); REQUIRE(wolfcert_key_generate(&kcfg, &rk) == WOLFCERT_OK); REQUIRE(wolfcert_est_simple_reenroll(&srv, dummy_csr, sizeof(dummy_csr), rk, - dummy_csr, sizeof(dummy_csr), &out) + dummy_csr, sizeof(dummy_csr), &res) == WOLFCERT_ERR_BAD_ARG); wolfcert_key_free(rk); @@ -431,45 +433,45 @@ static int test_est_result_defined_on_early_return(void) REQUIRE(wolfcert_key_generate(&kcfg, &key) == WOLFCERT_OK); memset(&r, 0xA5, sizeof(r)); - if (est_result_defined("simple_enroll_ex", - wolfcert_est_simple_enroll_ex(NULL, blob, sizeof(blob), &r), &r)) { + if (est_result_defined("simple_enroll", + wolfcert_est_simple_enroll(NULL, blob, sizeof(blob), &r), &r)) { wolfcert_key_free(key); return 1; } wolfcert_est_result_free(&r); memset(&r, 0xA5, sizeof(r)); - if (est_result_defined("simple_reenroll_ex", - wolfcert_est_simple_reenroll_ex(NULL, blob, sizeof(blob), key, - blob, sizeof(blob), &r), &r)) { + if (est_result_defined("simple_reenroll", + wolfcert_est_simple_reenroll(NULL, blob, sizeof(blob), key, + blob, sizeof(blob), &r), &r)) { wolfcert_key_free(key); return 1; } wolfcert_est_result_free(&r); memset(&r, 0xA5, sizeof(r)); - if (est_result_defined("session_simple_enroll_ex", - wolfcert_est_session_simple_enroll_ex(NULL, blob, sizeof(blob), - &r), &r)) { + if (est_result_defined("session_simple_enroll", + wolfcert_est_session_simple_enroll(NULL, blob, sizeof(blob), + &r), &r)) { wolfcert_key_free(key); return 1; } wolfcert_est_result_free(&r); memset(&r, 0xA5, sizeof(r)); - if (est_result_defined("session_simple_enroll_nb_ex", - wolfcert_est_session_simple_enroll_nb_ex(NULL, blob, sizeof(blob), - &r), &r)) { + if (est_result_defined("session_simple_enroll_nb", + wolfcert_est_session_simple_enroll_nb(NULL, blob, sizeof(blob), + &r), &r)) { wolfcert_key_free(key); return 1; } wolfcert_est_result_free(&r); - if (wolfcert_est_session_simple_enroll_ex(NULL, blob, sizeof(blob), NULL) + if (wolfcert_est_session_simple_enroll(NULL, blob, sizeof(blob), NULL) != WOLFCERT_ERR_BAD_ARG || - wolfcert_est_session_simple_enroll_nb_ex(NULL, blob, sizeof(blob), NULL) + wolfcert_est_session_simple_enroll_nb(NULL, blob, sizeof(blob), NULL) != WOLFCERT_ERR_BAD_ARG) { - fprintf(stderr, "FAIL session enroll _ex: NULL out not rejected\n"); + fprintf(stderr, "FAIL session enroll: NULL out not rejected\n"); wolfcert_key_free(key); return 1; } @@ -499,7 +501,7 @@ static int test_est_uses_cfg_transport(void) /* Drives a non-blocking session enroll, polling the session fd. */ static int pump_simple_enroll(WolfCertEstSession* s, const uint8_t* csr, size_t csr_len, - WolfCertBuffer* out) + WolfCertEstResult* out) { int fd = wolfcert_est_session_fd(s); for (;;) { @@ -529,6 +531,7 @@ static int check_empty_body(WOLFSSL_CTX* ctx, const WolfCertServerCfg* tmpl, WolfCertServerCfg srv = *tmpl; WolfCertEstSession* sess = NULL; WolfCertBuffer out = { 0 }; + WolfCertEstResult res = { 0 }; pthread_t tid; char url[128]; int port = 0; @@ -543,12 +546,12 @@ static int check_empty_body(WOLFSSL_CTX* ctx, const WolfCertServerCfg* tmpl, REQUIRE(out.data == NULL); /* A one-shot closes after its request; a session keeps the connection. */ REQUIRE(strstr(sc.request, "Connection: close\r\n") != NULL); - REQUIRE(wolfcert_est_simple_enroll(&srv, csr, csr_len, &out) + REQUIRE(wolfcert_est_simple_enroll(&srv, csr, csr_len, &res) == WOLFCERT_ERR_HTTP); - REQUIRE(out.data == NULL); + REQUIRE(res.cert_pem.data == NULL); REQUIRE(wolfcert_est_simple_reenroll(&srv, cur, cur_len, dk, csr, csr_len, - &out) == WOLFCERT_ERR_HTTP); - REQUIRE(out.data == NULL); + &res) == WOLFCERT_ERR_HTTP); + REQUIRE(res.cert_pem.data == NULL); REQUIRE(wolfcert_est_session_open(&srv, &sess) == WOLFCERT_OK); REQUIRE(wolfcert_est_session_get_cacerts(sess, &out) == WOLFCERT_ERR_HTTP); @@ -558,10 +561,10 @@ static int check_empty_body(WOLFSSL_CTX* ctx, const WolfCertServerCfg* tmpl, sess = NULL; REQUIRE(wolfcert_est_session_open(&srv, &sess) == WOLFCERT_OK); - REQUIRE(wolfcert_est_session_simple_enroll(sess, csr, csr_len, &out) + REQUIRE(wolfcert_est_session_simple_enroll(sess, csr, csr_len, &res) == WOLFCERT_ERR_HTTP); wolfcert_est_session_close(sess); - REQUIRE(out.data == NULL); + REQUIRE(res.cert_pem.data == NULL); pthread_join(tid, NULL); return 0; @@ -682,36 +685,38 @@ int main(void) WolfCertBuffer csr = { 0 }; REQUIRE(wolfcert_csr_build(dk, &meta, &csr) == WOLFCERT_OK); - WolfCertBuffer enrolled = { 0 }; + WolfCertEstResult enrolled = { 0 }; REQUIRE(wolfcert_est_simple_enroll(&srv, csr.data, csr.len, &enrolled) == WOLFCERT_OK); - REQUIRE(memmem(enrolled.data, enrolled.len, "BEGIN CERTIFICATE", 17) != NULL); - wolfcert_buffer_free(&enrolled); + REQUIRE(memmem(enrolled.cert_pem.data, enrolled.cert_pem.len, + "BEGIN CERTIFICATE", 17) != NULL); + wolfcert_est_result_free(&enrolled); - WolfCertBuffer reenrolled = { 0 }; + WolfCertEstResult reenrolled = { 0 }; REQUIRE(wolfcert_est_simple_reenroll(&srv, ca_der, ca_len, dk, - csr.data, csr.len, &reenrolled) == WOLFCERT_OK); - wolfcert_buffer_free(&reenrolled); + csr.data, csr.len, &reenrolled) == WOLFCERT_OK + && reenrolled.status == WOLFCERT_EST_STATUS_SUCCESS); + wolfcert_est_result_free(&reenrolled); /* Session enroll sends the base64 CTE header (RFC 7030 section 4.2.1). */ WolfCertEstSession* sess = NULL; REQUIRE(wolfcert_est_session_open(&srv, &sess) == WOLFCERT_OK); - WolfCertBuffer sess_enrolled = { 0 }; + WolfCertEstResult sess_enrolled = { 0 }; REQUIRE(wolfcert_est_session_simple_enroll(sess, csr.data, csr.len, &sess_enrolled) == WOLFCERT_OK); - REQUIRE(memmem(sess_enrolled.data, sess_enrolled.len, + REQUIRE(memmem(sess_enrolled.cert_pem.data, sess_enrolled.cert_pem.len, "BEGIN CERTIFICATE", 17) != NULL); - wolfcert_buffer_free(&sess_enrolled); + wolfcert_est_result_free(&sess_enrolled); wolfcert_est_session_close(sess); /* Non-blocking session enroll also sends the base64 CTE header. */ WolfCertEstSession* sess_nb = NULL; REQUIRE(wolfcert_est_session_open_async(&srv, &sess_nb) == WOLFCERT_OK); - WolfCertBuffer sess_nb_enrolled = { 0 }; + WolfCertEstResult sess_nb_enrolled = { 0 }; REQUIRE(pump_simple_enroll(sess_nb, csr.data, csr.len, &sess_nb_enrolled) == 0); - REQUIRE(memmem(sess_nb_enrolled.data, sess_nb_enrolled.len, + REQUIRE(memmem(sess_nb_enrolled.cert_pem.data, sess_nb_enrolled.cert_pem.len, "BEGIN CERTIFICATE", 17) != NULL); - wolfcert_buffer_free(&sess_nb_enrolled); + wolfcert_est_result_free(&sess_nb_enrolled); wolfcert_est_session_close(sess_nb); REQUIRE(check_empty_body(ctx, &srv, csr.data, csr.len, ca_der, ca_len, diff --git a/tests/unit/test_scep_msg.c b/tests/unit/test_scep_msg.c index b102679..948e382 100644 --- a/tests/unit/test_scep_msg.c +++ b/tests/unit/test_scep_msg.c @@ -1013,32 +1013,32 @@ static int test_zero_length_args_rejected(void) wolfcert_scep_result_free(&r); \ } while (0) - REJECTS("pkcs_req_ex ra_cert_len", - wolfcert_scep_pkcs_req_ex(&srv, &caps, blob, 0, - blob, sizeof(blob), key, blob, sizeof(blob), &r)); - REJECTS("pkcs_req_ex ca_bundle_len", - wolfcert_scep_pkcs_req_ex(&srv, &caps, blob, sizeof(blob), - blob, 0, key, blob, sizeof(blob), &r)); - REJECTS("pkcs_req_ex csr_der_len", - wolfcert_scep_pkcs_req_ex(&srv, &caps, blob, sizeof(blob), - blob, sizeof(blob), key, blob, 0, &r)); - - REJECTS("renewal_req_ex ra_cert_len", - wolfcert_scep_renewal_req_ex(&srv, &caps, blob, 0, - blob, sizeof(blob), blob, sizeof(blob), - key, blob, sizeof(blob), &r)); - REJECTS("renewal_req_ex ca_bundle_len", - wolfcert_scep_renewal_req_ex(&srv, &caps, blob, sizeof(blob), - blob, 0, blob, sizeof(blob), - key, blob, sizeof(blob), &r)); - REJECTS("renewal_req_ex current_cert_len", - wolfcert_scep_renewal_req_ex(&srv, &caps, blob, sizeof(blob), - blob, sizeof(blob), blob, 0, - key, blob, sizeof(blob), &r)); - REJECTS("renewal_req_ex csr_der_len", - wolfcert_scep_renewal_req_ex(&srv, &caps, blob, sizeof(blob), - blob, sizeof(blob), blob, sizeof(blob), - key, blob, 0, &r)); + REJECTS("pkcs_req ra_cert_len", + wolfcert_scep_pkcs_req(&srv, &caps, blob, 0, + blob, sizeof(blob), key, blob, sizeof(blob), &r)); + REJECTS("pkcs_req ca_bundle_len", + wolfcert_scep_pkcs_req(&srv, &caps, blob, sizeof(blob), + blob, 0, key, blob, sizeof(blob), &r)); + REJECTS("pkcs_req csr_der_len", + wolfcert_scep_pkcs_req(&srv, &caps, blob, sizeof(blob), + blob, sizeof(blob), key, blob, 0, &r)); + + REJECTS("renewal_req ra_cert_len", + wolfcert_scep_renewal_req(&srv, &caps, blob, 0, + blob, sizeof(blob), blob, sizeof(blob), + key, blob, sizeof(blob), &r)); + REJECTS("renewal_req ca_bundle_len", + wolfcert_scep_renewal_req(&srv, &caps, blob, sizeof(blob), + blob, 0, blob, sizeof(blob), + key, blob, sizeof(blob), &r)); + REJECTS("renewal_req current_cert_len", + wolfcert_scep_renewal_req(&srv, &caps, blob, sizeof(blob), + blob, sizeof(blob), blob, 0, + key, blob, sizeof(blob), &r)); + REJECTS("renewal_req csr_der_len", + wolfcert_scep_renewal_req(&srv, &caps, blob, sizeof(blob), + blob, sizeof(blob), blob, sizeof(blob), + key, blob, 0, &r)); REJECTS("get_cert_initial ra_cert_len", wolfcert_scep_get_cert_initial(&srv, &caps, blob, 0, @@ -1089,13 +1089,13 @@ static int test_result_defined_on_early_return(void) } while (0) /* One-shot entry points with a NULL srv. */ - POISON_AND_CALL("pkcs_req_ex", - wolfcert_scep_pkcs_req_ex(NULL, &caps, blob, sizeof(blob), - blob, sizeof(blob), key, blob, sizeof(blob), &r)); - POISON_AND_CALL("renewal_req_ex", - wolfcert_scep_renewal_req_ex(NULL, &caps, blob, sizeof(blob), - blob, sizeof(blob), blob, sizeof(blob), - key, blob, sizeof(blob), &r)); + POISON_AND_CALL("pkcs_req", + wolfcert_scep_pkcs_req(NULL, &caps, blob, sizeof(blob), + blob, sizeof(blob), key, blob, sizeof(blob), &r)); + POISON_AND_CALL("renewal_req", + wolfcert_scep_renewal_req(NULL, &caps, blob, sizeof(blob), + blob, sizeof(blob), blob, sizeof(blob), + key, blob, sizeof(blob), &r)); POISON_AND_CALL("get_cert_initial", wolfcert_scep_get_cert_initial(NULL, &caps, blob, sizeof(blob), blob, sizeof(blob), blob, sizeof(blob), @@ -1107,27 +1107,27 @@ static int test_result_defined_on_early_return(void) key, blob, sizeof(blob), &r)); /* Session entry points with a NULL session. */ - POISON_AND_CALL("session_pkcs_req_ex", - wolfcert_scep_session_pkcs_req_ex(NULL, &caps, blob, sizeof(blob), - blob, sizeof(blob), key, - blob, sizeof(blob), &r)); + POISON_AND_CALL("session_pkcs_req", + wolfcert_scep_session_pkcs_req(NULL, &caps, blob, sizeof(blob), + blob, sizeof(blob), key, + blob, sizeof(blob), &r)); POISON_AND_CALL("session_pkcs_req_nb", wolfcert_scep_session_pkcs_req_nb(NULL, &caps, blob, sizeof(blob), blob, sizeof(blob), key, blob, sizeof(blob), &r)); - POISON_AND_CALL("session_renewal_req_ex", - wolfcert_scep_session_renewal_req_ex(NULL, &caps, blob, sizeof(blob), - blob, sizeof(blob), blob, sizeof(blob), - key, blob, sizeof(blob), &r)); + POISON_AND_CALL("session_renewal_req", + wolfcert_scep_session_renewal_req(NULL, &caps, blob, sizeof(blob), + blob, sizeof(blob), blob, sizeof(blob), + key, blob, sizeof(blob), &r)); POISON_AND_CALL("session_renewal_req_nb", wolfcert_scep_session_renewal_req_nb(NULL, &caps, blob, sizeof(blob), blob, sizeof(blob), blob, sizeof(blob), key, blob, sizeof(blob), &r)); - POISON_AND_CALL("session_get_cert_initial_ex", - wolfcert_scep_session_get_cert_initial_ex(NULL, &caps, blob, sizeof(blob), - blob, sizeof(blob), blob, sizeof(blob), - key, blob, sizeof(blob), - blob, sizeof(blob), &r)); + POISON_AND_CALL("session_get_cert_initial", + wolfcert_scep_session_get_cert_initial(NULL, &caps, blob, sizeof(blob), + blob, sizeof(blob), blob, sizeof(blob), + key, blob, sizeof(blob), + blob, sizeof(blob), &r)); POISON_AND_CALL("session_get_cert_initial_nb", wolfcert_scep_session_get_cert_initial_nb(NULL, &caps, blob, sizeof(blob), blob, sizeof(blob), blob, sizeof(blob), diff --git a/wolfcert/client.h b/wolfcert/client.h index da3e38f..6134a9c 100644 --- a/wolfcert/client.h +++ b/wolfcert/client.h @@ -51,7 +51,8 @@ WOLFCERT_API int wolfcert_client_fetch_meta(WolfCertClient* client, /* Generate a new key (honoring cfg->dev_id for CryptoCb offloading), build * a CSR from meta, and enroll. On success out_key owns the key and - * out_cert_pem owns the issued certificate. */ + * out_cert_pem owns the issued certificate. A pending enrollment returns + * WOLFCERT_ERR_PENDING and hands out neither. */ WOLFCERT_API int wolfcert_client_enroll(WolfCertClient* client, const WolfCertServerCfg* srv, const WolfCertKeyCfg* key_cfg, @@ -61,7 +62,9 @@ WOLFCERT_API int wolfcert_client_enroll(WolfCertClient* client, /* Re-enroll using an existing cert/key; a NULL new_key_cfg reuses the key. * The CSR copies current_cert's Subject and SAN: WOLFCERT_ERR_BAD_ARG if meta - * sets them, _PARSE for a bad cert, _UNSUPPORTED if one cannot be carried. */ + * sets them, _PARSE for a bad cert, _UNSUPPORTED if one cannot be carried. + * A pending renewal returns WOLFCERT_ERR_PENDING and hands out neither + * out_key nor out_cert_pem. */ WOLFCERT_API int wolfcert_client_reenroll(WolfCertClient* client, const WolfCertServerCfg* srv, const uint8_t* current_cert, size_t current_cert_len, diff --git a/wolfcert/errors.h b/wolfcert/errors.h index 72b5e73..5a36d25 100644 --- a/wolfcert/errors.h +++ b/wolfcert/errors.h @@ -40,9 +40,9 @@ enum { WOLFCERT_ERR_PARSE = -10, WOLFCERT_ERR_NOT_FOUND = -11, WOLFCERT_ERR_UNSUPPORTED = -12, - /* Enrollment accepted but not issued yet (SCEP pkiStatus=PENDING, or EST - * 202 Accepted per RFC 7030 section 4.2.3). Calls that fill a result struct - * report it in its status instead. */ + /* Enrollment accepted but not issued yet (EST 202 Accepted, RFC 7030 + * section 4.2.3), from wolfcert_client_enroll and _reenroll. Calls that + * fill a result struct report it in its status instead. */ WOLFCERT_ERR_PENDING = -13, /* Returned by the _nb calls when the socket would block. The same call diff --git a/wolfcert/est.h b/wolfcert/est.h index 89f618c..043e80e 100644 --- a/wolfcert/est.h +++ b/wolfcert/est.h @@ -103,16 +103,14 @@ WOLFCERT_API int wolfcert_csr_attrs_build(const WolfCertCsrAttrItem* items, size_t count, WolfCertBuffer* out_der); -/* Outcome of the _ex enroll calls (RFC 7030 section 4.2), matching the +/* Outcome of the enroll calls (RFC 7030 section 4.2), matching the * WolfCertScepStatus values: * SUCCESS - returns WOLFCERT_OK; cert_pem holds the issued cert. * PENDING - returns WOLFCERT_OK; cert_pem is empty and the caller re-POSTs * the identical request after retry_after_sec (section 4.2.3). * FAILURE - any other HTTP status; returns WOLFCERT_ERR_AUTH for 401/403, * else WOLFCERT_ERR_HTTP. - * UNSET - no usable reply; the int return code says why. - * The non-_ex calls return PENDING as WOLFCERT_ERR_PENDING and drop - * retry_after_sec. */ + * UNSET - no usable reply; the int return code says why. */ typedef enum { WOLFCERT_EST_STATUS_UNSET = 0, WOLFCERT_EST_STATUS_SUCCESS = 1, @@ -138,11 +136,7 @@ WOLFCERT_API void wolfcert_est_result_free(WolfCertEstResult* r); /* POST /.well-known/est/simpleenroll. */ WOLFCERT_API int wolfcert_est_simple_enroll(const WolfCertServerCfg* srv, const uint8_t* csr_der, size_t csr_der_len, - WolfCertBuffer* out_cert_pem); - -WOLFCERT_API int wolfcert_est_simple_enroll_ex(const WolfCertServerCfg* srv, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertEstResult* out); + WolfCertEstResult* out); /* POST /.well-known/est/simplereenroll. Uses the caller's current cert/key * as the TLS client credential. */ @@ -150,13 +144,7 @@ WOLFCERT_API int wolfcert_est_simple_reenroll(const WolfCertServerCfg* srv, const uint8_t* current_cert, size_t current_cert_len, const WolfCertKey* current_key, const uint8_t* csr_der, size_t csr_der_len, - WolfCertBuffer* out_cert_pem); - -WOLFCERT_API int wolfcert_est_simple_reenroll_ex(const WolfCertServerCfg* srv, - const uint8_t* current_cert, size_t current_cert_len, - const WolfCertKey* current_key, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertEstResult* out); + WolfCertEstResult* out); /* Keep-alive EST session carrying several requests over one TLS connection. * With proto_opts.est.allow_post_handshake_auth set, the server can request @@ -182,20 +170,16 @@ WOLFCERT_API int wolfcert_est_session_get_csr_attrs(WolfCertEstSession* s, WolfCertBuffer* out_attrs_der); WOLFCERT_API int wolfcert_est_session_simple_enroll(WolfCertEstSession* s, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertBuffer* out_cert_pem); - -WOLFCERT_API int wolfcert_est_session_simple_enroll_ex(WolfCertEstSession* s, - const uint8_t* csr_der, - size_t csr_der_len, - WolfCertEstResult* out); + const uint8_t* csr_der, + size_t csr_der_len, + WolfCertEstResult* out); /* The session's TLS client credential, srv->client_cert and client_key at * open, is the cert being renewed; WOLFCERT_ERR_BAD_ARG without one. */ -WOLFCERT_API int wolfcert_est_session_simple_reenroll_ex(WolfCertEstSession* s, - const uint8_t* csr_der, - size_t csr_der_len, - WolfCertEstResult* out); +WOLFCERT_API int wolfcert_est_session_simple_reenroll(WolfCertEstSession* s, + const uint8_t* csr_der, + size_t csr_der_len, + WolfCertEstResult* out); WOLFCERT_API void wolfcert_est_session_close(WolfCertEstSession* s); @@ -225,17 +209,12 @@ WOLFCERT_API int wolfcert_est_session_get_csr_attrs_nb(WolfCertEstSession* s, WOLFCERT_API int wolfcert_est_session_simple_enroll_nb(WolfCertEstSession* s, const uint8_t* csr_der, size_t csr_der_len, - WolfCertBuffer* out_cert_pem); - -WOLFCERT_API int wolfcert_est_session_simple_enroll_nb_ex(WolfCertEstSession* s, - const uint8_t* csr_der, - size_t csr_der_len, - WolfCertEstResult* out); + WolfCertEstResult* out); -WOLFCERT_API int wolfcert_est_session_simple_reenroll_nb_ex(WolfCertEstSession* s, - const uint8_t* csr_der, - size_t csr_der_len, - WolfCertEstResult* out); +WOLFCERT_API int wolfcert_est_session_simple_reenroll_nb(WolfCertEstSession* s, + const uint8_t* csr_der, + size_t csr_der_len, + WolfCertEstResult* out); #ifdef __cplusplus } diff --git a/wolfcert/scep.h b/wolfcert/scep.h index 560c91f..124d888 100644 --- a/wolfcert/scep.h +++ b/wolfcert/scep.h @@ -137,42 +137,24 @@ WOLFCERT_API void wolfcert_scep_result_free(WolfCertScepResult* r); * for a single-cert CA is ra_cert itself. Returns WOLFCERT_OK once a CertRep * verifies, with SUCCESS / PENDING / FAILURE in out->status; TLS, HTTP and * parse failures return their error code. */ -WOLFCERT_API int wolfcert_scep_pkcs_req_ex(const WolfCertServerCfg* srv, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const WolfCertKey* new_key, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertScepResult* out); - -/* PKCSReq, simple-result form. Returns WOLFCERT_ERR_PENDING when the server - * replies pkiStatus=3 and WOLFCERT_ERR_PROTOCOL on pkiStatus=2. PENDING - * carries no transactionID to poll with; the _ex form returns one. */ WOLFCERT_API int wolfcert_scep_pkcs_req(const WolfCertServerCfg* srv, const WolfCertScepCaps* caps, const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, const WolfCertKey* new_key, const uint8_t* csr_der, size_t csr_der_len, - WolfCertBuffer* out_cert_pem); + WolfCertScepResult* out); /* RenewalReq: current_key signs the pkiMessage and csr_der carries the new - * public key. ra_cert and ca_bundle are as for wolfcert_scep_pkcs_req_ex. */ -WOLFCERT_API int wolfcert_scep_renewal_req_ex(const WolfCertServerCfg* srv, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const uint8_t* current_cert, size_t current_cert_len, - const WolfCertKey* current_key, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertScepResult* out); - + * public key. ra_cert and ca_bundle are as for wolfcert_scep_pkcs_req. */ WOLFCERT_API int wolfcert_scep_renewal_req(const WolfCertServerCfg* srv, const WolfCertScepCaps* caps, const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, const uint8_t* current_cert, size_t current_cert_len, const WolfCertKey* current_key, const uint8_t* csr_der, size_t csr_der_len, - WolfCertBuffer* out_cert_pem); + WolfCertScepResult* out); /* GetCertInitial (RFC 8894 section 3.3.3): poll a PKCSReq or RenewalReq that * returned PENDING. signer_cert is NULL for a PKCSReq (the transient @@ -180,7 +162,7 @@ WOLFCERT_API int wolfcert_scep_renewal_req(const WolfCertServerCfg* srv, * a RenewalReq. * transaction_id is the prior request's, echoed verbatim; a value outside * PrintableString returns WOLFCERT_ERR_BAD_ARG. ra_cert and ca_bundle are as - * for wolfcert_scep_pkcs_req_ex. */ + * for wolfcert_scep_pkcs_req. */ WOLFCERT_API int wolfcert_scep_get_cert_initial(const WolfCertServerCfg* srv, const WolfCertScepCaps* caps, const uint8_t* ra_cert, size_t ra_cert_len, @@ -197,7 +179,7 @@ WOLFCERT_API int wolfcert_scep_get_cert_initial(const WolfCertServerCfg* srv, * FAILURE/badCertId or not at all. signer_cert / signer_key sign the * pkiMessage, with no self-signed fallback. serial is the INTEGER content * (DecodedCert.serial / serialSz). ra_cert and ca_bundle are as for - * wolfcert_scep_pkcs_req_ex. On a hit out->status is SUCCESS; a verified reply + * wolfcert_scep_pkcs_req. On a hit out->status is SUCCESS; a verified reply * without the requested issuer and serial returns WOLFCERT_ERR_PROTOCOL with * out->status UNSET, and running out of memory searching it returns * WOLFCERT_ERR_MEMORY. */ @@ -221,8 +203,8 @@ WOLFCERT_API int wolfcert_scep_get_next_ca_cert(const WolfCertServerCfg* srv, /* Keep-alive SCEP session carrying several PKIOperation round trips; GetCACaps * and GetCACert stay one-shot calls. Plain http:// is accepted and https:// - * needs srv->verify_server. The _ex calls need wolfcert_scep_session_open and - * the _nb calls wolfcert_scep_session_open_async; a mismatch returns + * needs srv->verify_server. The blocking calls need wolfcert_scep_session_open + * and the _nb calls wolfcert_scep_session_open_async; a mismatch returns * WOLFCERT_ERR_BAD_ARG. A _nb call returning WANT_READ / WANT_WRITE is repeated * with the same arguments; a call with a different out pointer returns * WOLFCERT_ERR_BAD_ARG. DNS and the initial connect stay synchronous. Over @@ -242,13 +224,13 @@ WOLFCERT_API void wolfcert_scep_session_close(WolfCertScepSession* s); WOLFCERT_API int wolfcert_scep_session_fd(const WolfCertScepSession* s); /* PKCSReq over the session; arguments and result as for - * wolfcert_scep_pkcs_req_ex. */ -WOLFCERT_API int wolfcert_scep_session_pkcs_req_ex(WolfCertScepSession* s, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const WolfCertKey* new_key, const uint8_t* csr_der, size_t csr_der_len, - WolfCertScepResult* out); + * wolfcert_scep_pkcs_req. */ +WOLFCERT_API int wolfcert_scep_session_pkcs_req(WolfCertScepSession* s, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const WolfCertKey* new_key, const uint8_t* csr_der, size_t csr_der_len, + WolfCertScepResult* out); WOLFCERT_API int wolfcert_scep_session_pkcs_req_nb(WolfCertScepSession* s, const WolfCertScepCaps* caps, const uint8_t* ra_cert, size_t ra_cert_len, @@ -256,14 +238,14 @@ WOLFCERT_API int wolfcert_scep_session_pkcs_req_nb(WolfCertScepSession* s, const WolfCertKey* new_key, const uint8_t* csr_der, size_t csr_der_len, WolfCertScepResult* out); -/* RenewalReq over the session; see wolfcert_scep_renewal_req_ex. */ -WOLFCERT_API int wolfcert_scep_session_renewal_req_ex(WolfCertScepSession* s, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const uint8_t* current_cert, size_t current_cert_len, - const WolfCertKey* current_key, const uint8_t* csr_der, size_t csr_der_len, - WolfCertScepResult* out); +/* RenewalReq over the session; see wolfcert_scep_renewal_req. */ +WOLFCERT_API int wolfcert_scep_session_renewal_req(WolfCertScepSession* s, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const uint8_t* current_cert, size_t current_cert_len, + const WolfCertKey* current_key, const uint8_t* csr_der, size_t csr_der_len, + WolfCertScepResult* out); WOLFCERT_API int wolfcert_scep_session_renewal_req_nb(WolfCertScepSession* s, const WolfCertScepCaps* caps, const uint8_t* ra_cert, size_t ra_cert_len, @@ -273,15 +255,15 @@ WOLFCERT_API int wolfcert_scep_session_renewal_req_nb(WolfCertScepSession* s, WolfCertScepResult* out); /* GetCertInitial over the session; see wolfcert_scep_get_cert_initial. */ -WOLFCERT_API int wolfcert_scep_session_get_cert_initial_ex(WolfCertScepSession* s, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const uint8_t* signer_cert, size_t signer_cert_len, - const WolfCertKey* signer_key, - const uint8_t* csr_der, size_t csr_der_len, - const uint8_t* transaction_id, size_t transaction_id_len, - WolfCertScepResult* out); +WOLFCERT_API int wolfcert_scep_session_get_cert_initial(WolfCertScepSession* s, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const uint8_t* signer_cert, size_t signer_cert_len, + const WolfCertKey* signer_key, + const uint8_t* csr_der, size_t csr_der_len, + const uint8_t* transaction_id, size_t transaction_id_len, + WolfCertScepResult* out); WOLFCERT_API int wolfcert_scep_session_get_cert_initial_nb(WolfCertScepSession* s, const WolfCertScepCaps* caps, const uint8_t* ra_cert, size_t ra_cert_len, From 44fb358010b32ab9633191c12566a41c680c124b Mon Sep 17 00:00:00 2001 From: Yosuke Shimizu Date: Tue, 20 Oct 2026 20:41:09 +0900 Subject: [PATCH 3/5] est: one session type - wolfcert_est_session_open_async() is removed; any session takes blocking and _nb calls, and a blocking call while a request is in flight gets WOLFCERT_ERR_BAD_ARG. - est_call_clear() zeroes an enroll result or a buffer output only when a call starts a request, and est.h says a resume passes it back unfreed; est_finish() clears an empty csrattrs result. - wolfcert_est_get_cacerts_enc() moves to est.h, and wolfcert_est_simple_reenroll() rejects an empty current_cert. - wolfcert_client_fetch_meta() calls wolfcert_client_auto_csrattrs(). --- src/client.c | 53 ++++------- src/est/est_client.c | 95 +++++++++---------- src/internal.h | 3 - tests/integration/test_est_async_roundtrip.c | 8 +- .../test_est_csr_attrs_roundtrip.c | 10 +- tests/integration/test_est_mtls_roundtrip.c | 2 - .../integration/test_est_pending_roundtrip.c | 14 ++- tests/unit/test_est.c | 10 +- wolfcert/est.h | 23 +++-- 9 files changed, 101 insertions(+), 117 deletions(-) diff --git a/src/client.c b/src/client.c index 0bce577..771885a 100644 --- a/src/client.c +++ b/src/client.c @@ -76,42 +76,6 @@ int wolfcert_client_get_ca(WolfCertClient* client, const WolfCertServerCfg* srv, return WOLFCERT_ERR_UNSUPPORTED; } -int wolfcert_client_fetch_meta(WolfCertClient* client, const WolfCertServerCfg* srv, - WolfCertCertMeta* meta) -{ - (void)client; - if (srv == NULL) - return WOLFCERT_ERR_BAD_ARG; - -#ifdef WOLFCERT_HAVE_EST - if (srv->protocol == WOLFCERT_PROTO_EST) { - WolfCertBuffer raw = { 0 }; - int rc = wolfcert_est_get_csr_attrs(srv, &raw); - if (rc != WOLFCERT_OK) - return rc; - - if (raw.data == NULL || raw.len == 0) - return WOLFCERT_OK; - - WolfCertCsrAttrs attrs; - rc = wolfcert_est_parse_csr_attrs(raw.data, raw.len, &attrs); - wolfcert_buffer_free(&raw); - if (rc != WOLFCERT_OK) - return rc; - - if (meta != NULL) - rc = wolfcert_csr_attrs_apply(&attrs, NULL, meta); - - wolfcert_csr_attrs_free(&attrs); - return rc; - } -#endif - - /* SCEP has no /csrattrs equivalent. */ - (void)meta; - return WOLFCERT_ERR_UNSUPPORTED; -} - #ifdef WOLFCERT_HAVE_EST /* Overlay the server's /csrattrs hints onto key_cfg and meta. */ static int wolfcert_client_auto_csrattrs(const WolfCertServerCfg* srv, @@ -155,6 +119,23 @@ static int wolfcert_client_take_cert(int rc, WolfCertEstResult* r, } #endif +int wolfcert_client_fetch_meta(WolfCertClient* client, const WolfCertServerCfg* srv, + WolfCertCertMeta* meta) +{ + (void)client; + if (srv == NULL) + return WOLFCERT_ERR_BAD_ARG; + +#ifdef WOLFCERT_HAVE_EST + if (srv->protocol == WOLFCERT_PROTO_EST) + return wolfcert_client_auto_csrattrs(srv, NULL, meta); +#endif + + /* SCEP has no /csrattrs equivalent. */ + (void)meta; + return WOLFCERT_ERR_UNSUPPORTED; +} + int wolfcert_client_enroll(WolfCertClient* client, const WolfCertServerCfg* srv, const WolfCertKeyCfg* key_cfg, const WolfCertCertMeta* meta, diff --git a/src/est/est_client.c b/src/est/est_client.c index 298651a..477572c 100644 --- a/src/est/est_client.c +++ b/src/est/est_client.c @@ -17,8 +17,6 @@ * along with wolfCert. If not, see . */ -#define _POSIX_C_SOURCE 200809L - #include #include #include @@ -26,7 +24,6 @@ #include -#include #include enum est_session_op { @@ -46,7 +43,7 @@ static const struct { { "simplereenroll", "application/pkcs7-mime" } }; -static int est_is_enroll(int op) +static int est_is_enroll(enum est_session_op op) { return op == EST_OP_SIMPLE_ENROLL || op == EST_OP_SIMPLE_REENROLL; } @@ -70,7 +67,8 @@ static char* join_path(const char* base, const char* suffix, void* heap) /* Parse the response to op into *out and free resp. res takes an enroll op's * status and is NULL for the other ops. */ -static int est_finish(void* heap, WolfCertHttpResponse* resp, int op, +static int est_finish(void* heap, WolfCertHttpResponse* resp, + enum est_session_op op, WolfCertEncoding enc, WolfCertBuffer* out, WolfCertEstResult* res) { @@ -88,7 +86,7 @@ static int est_finish(void* heap, WolfCertHttpResponse* resp, int op, else if (op == EST_OP_GET_CSRATTRS && (status == 204 || status == 404 || status == 405 || status == 501 || (status == 200 && resp->body_len == 0))) { - rc = WOLFCERT_OK; + memset(out, 0, sizeof(*out)); } else if (status != 200) { if (res != NULL) @@ -154,12 +152,12 @@ struct WolfCertEstSession { WolfCertBuffer in_body; /* base64-encoded CSR for POST, owned */ WolfCertHttpRequest in_req; WolfCertHttpResponse in_resp; - const void* in_owner; /* caller's output, checked on resume */ + const void* in_out; /* caller's output, checked on resume */ enum est_session_op in_op; }; -static int est_session_open_common(const WolfCertServerCfg* srv, - WolfCertEstSession** out) +int wolfcert_est_session_open(const WolfCertServerCfg* srv, + WolfCertEstSession** out) { if (srv == NULL || srv->server_url == NULL || out == NULL) return WOLFCERT_ERR_BAD_ARG; @@ -196,7 +194,7 @@ static int est_session_open_common(const WolfCertServerCfg* srv, srv->client_key != NULL; s->base_url = wolfcert_strdup(srv->server_url, heap); if (s->base_url == NULL) { - WOLFCERT_XFREE(s, heap); + wolfcert_est_session_close(s); return WOLFCERT_ERR_MEMORY; } @@ -238,18 +236,6 @@ static int est_session_open_common(const WolfCertServerCfg* srv, return WOLFCERT_OK; } -int wolfcert_est_session_open(const WolfCertServerCfg* srv, - WolfCertEstSession** out) -{ - return est_session_open_common(srv, out); -} - -int wolfcert_est_session_open_async(const WolfCertServerCfg* srv, - WolfCertEstSession** out) -{ - return est_session_open_common(srv, out); -} - int wolfcert_est_session_fd(const WolfCertEstSession* s) { return s != NULL ? wolfcert_http_session_fd(s->http) : -1; @@ -264,23 +250,23 @@ static void est_session_free_request(WolfCertEstSession* s) memset(&s->in_req, 0, sizeof(s->in_req)); } -static void est_async_reset(WolfCertEstSession* s) +static void est_session_reset(WolfCertEstSession* s) { est_session_free_request(s); wolfcert_http_response_free(&s->in_resp); memset(&s->in_resp, 0, sizeof(s->in_resp)); - s->in_owner = NULL; + s->in_out = NULL; s->in_active = 0; } /* Refuse a resumed _nb call that does not match the request in flight. */ -static int est_session_resume_check(const WolfCertEstSession* s, int op, - const void* owner) +static int est_session_resume_check(const WolfCertEstSession* s, + enum est_session_op op, const void* out) { - if ((int)s->in_op != op) + if (s->in_op != op) return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "est", "a different EST operation is already in flight on this session"); - if (owner != s->in_owner) + if (out != s->in_out) return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "est", "output pointer differs from the in-flight request; pass the " "same pointer to each poll call"); @@ -292,7 +278,7 @@ void wolfcert_est_session_close(WolfCertEstSession* s) if (s == NULL) return; - est_async_reset(s); + est_session_reset(s); if (s->http) wolfcert_http_session_close(s->http); @@ -307,10 +293,23 @@ void wolfcert_est_session_close(WolfCertEstSession* s) WOLFCERT_XFREE(s, s->heap); } +/* Clear the caller's output, unless it is the in-flight request's. */ +static void est_call_clear(const WolfCertEstSession* s, + WolfCertBuffer* out, WolfCertEstResult* res) +{ + if (s != NULL && s->in_active && (const void*)out == s->in_out) + return; + + if (res != NULL) + memset(res, 0, sizeof(*res)); + else if (out != NULL) + memset(out, 0, sizeof(*out)); +} + /* Build the request for op into s->in_* and mark it in flight. */ -static int est_session_begin(WolfCertEstSession* s, int op, +static int est_session_begin(WolfCertEstSession* s, enum est_session_op op, const uint8_t* csr_der, size_t csr_der_len, - const void* owner) + const void* out) { int rc = WOLFCERT_OK; @@ -339,12 +338,12 @@ static int est_session_begin(WolfCertEstSession* s, int op, s->in_req.body = s->in_body.data; s->in_req.body_len = s->in_body.len; } - s->in_op = (enum est_session_op)op; - s->in_owner = owner; + s->in_op = op; + s->in_out = out; s->in_active = 1; } else { - est_async_reset(s); + est_session_reset(s); } return rc; @@ -353,20 +352,22 @@ static int est_session_begin(WolfCertEstSession* s, int op, /* Run op on s, or continue the _nb request in flight, and parse the response * into *out and, for an enroll op, *res. *out also identifies the caller * across _nb calls. */ -static int est_session_call(WolfCertEstSession* s, int op, +static int est_session_call(WolfCertEstSession* s, enum est_session_op op, const uint8_t* csr_der, size_t csr_der_len, WolfCertEncoding enc, WolfCertBuffer* out, WolfCertEstResult* res, int nb) { int rc; + est_call_clear(s, out, res); if (s == NULL || out == NULL || (est_is_enroll(op) && (csr_der == NULL || csr_der_len == 0))) return WOLFCERT_ERR_BAD_ARG; if (s->in_active && !nb) return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "est", - "an async EST request is in flight on this session"); + "an EST request is in flight on this session; continue it with " + "the _nb call"); if (s->in_active) rc = est_session_resume_check(s, op, out); @@ -393,7 +394,7 @@ static int est_session_call(WolfCertEstSession* s, int op, rc = est_finish(s->heap, &s->in_resp, op, enc, out, res); } - est_async_reset(s); + est_session_reset(s); return rc; } @@ -449,7 +450,6 @@ int wolfcert_est_session_simple_enroll(WolfCertEstSession* s, if (out == NULL) return WOLFCERT_ERR_BAD_ARG; - memset(out, 0, sizeof(*out)); return est_session_call(s, EST_OP_SIMPLE_ENROLL, csr_der, csr_der_len, WOLFCERT_ENCODING_PEM, &out->cert_pem, out, 0); } @@ -462,7 +462,6 @@ int wolfcert_est_session_simple_enroll_nb(WolfCertEstSession* s, if (out == NULL) return WOLFCERT_ERR_BAD_ARG; - memset(out, 0, sizeof(*out)); return est_session_call(s, EST_OP_SIMPLE_ENROLL, csr_der, csr_der_len, WOLFCERT_ENCODING_PEM, &out->cert_pem, out, 1); } @@ -475,7 +474,6 @@ int wolfcert_est_session_simple_reenroll(WolfCertEstSession* s, if (out == NULL) return WOLFCERT_ERR_BAD_ARG; - memset(out, 0, sizeof(*out)); return est_session_call(s, EST_OP_SIMPLE_REENROLL, csr_der, csr_der_len, WOLFCERT_ENCODING_PEM, &out->cert_pem, out, 0); } @@ -488,12 +486,11 @@ int wolfcert_est_session_simple_reenroll_nb(WolfCertEstSession* s, if (out == NULL) return WOLFCERT_ERR_BAD_ARG; - memset(out, 0, sizeof(*out)); return est_session_call(s, EST_OP_SIMPLE_REENROLL, csr_der, csr_der_len, WOLFCERT_ENCODING_PEM, &out->cert_pem, out, 1); } -static int est_one_shot(const WolfCertServerCfg* srv, int op, +static int est_one_shot(const WolfCertServerCfg* srv, enum est_session_op op, const uint8_t* csr_der, size_t csr_der_len, WolfCertEncoding enc, WolfCertBuffer* out, WolfCertEstResult* res) @@ -501,11 +498,12 @@ static int est_one_shot(const WolfCertServerCfg* srv, int op, WolfCertEstSession* s = NULL; int rc; + est_call_clear(NULL, out, res); if (out == NULL || (est_is_enroll(op) && (csr_der == NULL || csr_der_len == 0))) return WOLFCERT_ERR_BAD_ARG; - rc = est_session_open_common(srv, &s); + rc = wolfcert_est_session_open(srv, &s); if (rc == WOLFCERT_OK) { wolfcert_http_session_set_close(s->http); rc = est_session_call(s, op, csr_der, csr_der_len, enc, out, res, 0); @@ -530,11 +528,6 @@ int wolfcert_est_get_cacerts_enc(const WolfCertServerCfg* srv, WolfCertEncoding int wolfcert_est_get_csr_attrs(const WolfCertServerCfg* srv, WolfCertBuffer* out_attrs_der) { - if (srv == NULL || out_attrs_der == NULL) - return WOLFCERT_ERR_BAD_ARG; - - memset(out_attrs_der, 0, sizeof(*out_attrs_der)); - out_attrs_der->heap = srv->heap ? srv->heap : wolfcert_default_heap(); return est_one_shot(srv, EST_OP_GET_CSRATTRS, NULL, 0, WOLFCERT_ENCODING_DER, out_attrs_der, NULL); } @@ -546,7 +539,6 @@ int wolfcert_est_simple_enroll(const WolfCertServerCfg* srv, if (out == NULL) return WOLFCERT_ERR_BAD_ARG; - memset(out, 0, sizeof(*out)); return est_one_shot(srv, EST_OP_SIMPLE_ENROLL, csr_der, csr_der_len, WOLFCERT_ENCODING_PEM, &out->cert_pem, out); } @@ -564,9 +556,10 @@ int wolfcert_est_simple_reenroll(const WolfCertServerCfg* srv, if (out == NULL) return WOLFCERT_ERR_BAD_ARG; - memset(out, 0, sizeof(*out)); + est_call_clear(NULL, &out->cert_pem, out); - if (srv == NULL || current_cert == NULL || current_key == NULL) + if (srv == NULL || current_cert == NULL || current_cert_len == 0 || + current_key == NULL) return WOLFCERT_ERR_BAD_ARG; /* The TLS identity is the cert being renewed. */ diff --git a/src/internal.h b/src/internal.h index a6386b9..bac5300 100644 --- a/src/internal.h +++ b/src/internal.h @@ -349,9 +349,6 @@ WOLFCERT_TEST_VIS int wolfcert_pkcs7_certs_to_pem(const uint8_t* p7_der, size_t WOLFCERT_TEST_VIS int wolfcert_pkcs7_certs_to_der(const uint8_t* p7_der, size_t p7_der_len, WolfCertBuffer* out_der, void* heap); -WOLFCERT_TEST_VIS int wolfcert_est_get_cacerts_enc(const WolfCertServerCfg* srv, - WolfCertEncoding enc, - WolfCertBuffer* out_ca); WOLFCERT_TEST_VIS int wolfcert_pkcs7_build_certs_only(const uint8_t* const* certs_der, const size_t* certs_len, size_t count, WolfCertBuffer* out_der, void* heap); diff --git a/tests/integration/test_est_async_roundtrip.c b/tests/integration/test_est_async_roundtrip.c index eab41a0..6a70f1f 100644 --- a/tests/integration/test_est_async_roundtrip.c +++ b/tests/integration/test_est_async_roundtrip.c @@ -156,7 +156,7 @@ int main(void) .proto_opts.est = { .allow_post_handshake_auth = 1 }, }; WolfCertEstSession* es = NULL; - REQUIRE(wolfcert_est_session_open_async(&cli, &es) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_open(&cli, &es) == WOLFCERT_OK); REQUIRE(wolfcert_est_session_fd(es) >= 0); WolfCertBuffer ca_pem = { 0 }; @@ -179,7 +179,7 @@ int main(void) wolfcert_server_free(srv); } - /* Basic-only server: cacerts and enroll on one async session as alice. */ + /* Basic-only server: _nb cacerts and enroll on one session as alice. */ WolfCertServerCfgSrv bcfg = { .protocol = WOLFCERT_PROTO_EST, .bind_host = "127.0.0.1", @@ -208,7 +208,7 @@ int main(void) .verify_server = 1, }; WolfCertEstSession* bes = NULL; - REQUIRE(wolfcert_est_session_open_async(&bcli, &bes) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_open(&bcli, &bes) == WOLFCERT_OK); WolfCertBuffer bca = { 0 }; WolfCertEstResult bissued = { 0 }; @@ -227,7 +227,7 @@ int main(void) WolfCertServerCfg wcli = bcli; wcli.proto_opts.est.password = "wrong"; bes = NULL; - REQUIRE(wolfcert_est_session_open_async(&wcli, &bes) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_open(&wcli, &bes) == WOLFCERT_OK); WolfCertEstResult wr = { 0 }; REQUIRE(pump_simple_enroll(bes, csr.data, csr.len, &wr) diff --git a/tests/integration/test_est_csr_attrs_roundtrip.c b/tests/integration/test_est_csr_attrs_roundtrip.c index 53326f0..596604f 100644 --- a/tests/integration/test_est_csr_attrs_roundtrip.c +++ b/tests/integration/test_est_csr_attrs_roundtrip.c @@ -132,11 +132,9 @@ int main(void) int rc; REQUIRE(wolfcert_est_session_open(&cli, &sess) == WOLFCERT_OK); REQUIRE(wolfcert_est_session_get_csr_attrs(sess, &sraw) == WOLFCERT_OK); - wolfcert_est_session_close(sess); REQUIRE(sraw.len == blob.len && memcmp(sraw.data, blob.data, blob.len) == 0); wolfcert_buffer_free(&sraw); - REQUIRE(wolfcert_est_session_open_async(&cli, &sess) == WOLFCERT_OK); while ((rc = wolfcert_est_session_get_csr_attrs_nb(sess, &sraw)) == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE) REQUIRE(wait_io(sess, rc) == 0); @@ -203,6 +201,14 @@ int main(void) REQUIRE(empty.data == NULL); REQUIRE(empty.len == 0); + /* An empty session reply clears the output too, whatever it held. */ + REQUIRE(wolfcert_est_session_open(&cli2, &sess) == WOLFCERT_OK); + memset(&empty, 0xA5, sizeof(empty)); + REQUIRE(wolfcert_est_session_get_csr_attrs(sess, &empty) == WOLFCERT_OK); + wolfcert_est_session_close(sess); + REQUIRE(empty.data == NULL); + REQUIRE(empty.len == 0); + /* The stray path segment makes the server answer 404 for /csrattrs. */ char url404[160]; snprintf(url404, sizeof(url404), diff --git a/tests/integration/test_est_mtls_roundtrip.c b/tests/integration/test_est_mtls_roundtrip.c index 99391c4..d1927a7 100644 --- a/tests/integration/test_est_mtls_roundtrip.c +++ b/tests/integration/test_est_mtls_roundtrip.c @@ -122,11 +122,9 @@ static int test_reenroll_ignores_cfg_identity(const char* url, cli.client_key_len = cli_key_len; REQUIRE(wolfcert_est_session_open(&cli, &sess) == WOLFCERT_OK); rc = wolfcert_est_session_simple_reenroll(sess, csr.data, csr.len, &res); - wolfcert_est_session_close(sess); REQUIRE(rc == WOLFCERT_OK && res.status == WOLFCERT_EST_STATUS_SUCCESS); wolfcert_est_result_free(&res); - REQUIRE(wolfcert_est_session_open_async(&cli, &sess) == WOLFCERT_OK); while ((rc = wolfcert_est_session_simple_reenroll_nb(sess, csr.data, csr.len, &res)) == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE) diff --git a/tests/integration/test_est_pending_roundtrip.c b/tests/integration/test_est_pending_roundtrip.c index 59bcf13..338194b 100644 --- a/tests/integration/test_est_pending_roundtrip.c +++ b/tests/integration/test_est_pending_roundtrip.c @@ -289,7 +289,7 @@ static int pending_path(WolfCertServer* s) REQUIRE(make_csr("CN=device-est-pending-async-ex", &dk_nex, &csr_nex) == 0); WolfCertEstSession* nsess = NULL; - REQUIRE(wolfcert_est_session_open_async(&cli, &nsess) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_open(&cli, &nsess) == WOLFCERT_OK); WolfCertEstResult nr1 = { 0 }; rc = pump_enroll_nb(nsess, csr_nex.data, csr_nex.len, &nr1); @@ -308,7 +308,7 @@ static int pending_path(WolfCertServer* s) "BEGIN CERTIFICATE", 17) != NULL); wolfcert_est_result_free(&nr2); - /* A blocking call works on the same session from _open_async. */ + /* A blocking call works on the session after its _nb requests. */ WolfCertBuffer async_ca = { 0 }; REQUIRE(wolfcert_est_session_get_cacerts(nsess, &async_ca) == WOLFCERT_OK); REQUIRE(async_ca.len > 0); @@ -336,7 +336,7 @@ static int pending_path(WolfCertServer* s) /* With /cacerts in flight, an enroll or a /cacerts call with a different * output buffer gets BAD_ARG. */ WolfCertEstSession* msess = NULL; - REQUIRE(wolfcert_est_session_open_async(&bh_cli, &msess) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_open(&bh_cli, &msess) == WOLFCERT_OK); WolfCertBuffer mix_ca = { 0 }; rc = wolfcert_est_session_get_cacerts_nb(msess, &mix_ca); REQUIRE(rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE); @@ -369,7 +369,7 @@ static int pending_path(WolfCertServer* s) /* With an enroll in flight, /cacerts or an enroll with a different output * gets BAD_ARG. */ msess = NULL; - REQUIRE(wolfcert_est_session_open_async(&bh_cli, &msess) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_open(&bh_cli, &msess) == WOLFCERT_OK); /* A missing CSR is refused without starting a request. */ REQUIRE(wolfcert_est_session_simple_enroll_nb(msess, NULL, csr_mix.len, @@ -395,15 +395,19 @@ static int pending_path(WolfCertServer* s) csr_mix.len, &other_r) == WOLFCERT_ERR_BAD_ARG); + /* A resume leaves the in-flight result as it is; 42 is a sentinel a + * clear would reset. */ + mr.retry_after_sec = 42; rc = wolfcert_est_session_simple_enroll_nb(msess, csr_mix.data, csr_mix.len, &mr); REQUIRE(rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE); + REQUIRE(mr.retry_after_sec == 42); wolfcert_est_session_close(msess); close(bh_fd); /* A finished enroll frees the session for /cacerts. */ msess = NULL; - REQUIRE(wolfcert_est_session_open_async(&cli, &msess) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_open(&cli, &msess) == WOLFCERT_OK); REQUIRE(pump_enroll_nb(msess, csr_mix.data, csr_mix.len, &mr) == WOLFCERT_OK); REQUIRE(mr.status == WOLFCERT_EST_STATUS_PENDING); diff --git a/tests/unit/test_est.c b/tests/unit/test_est.c index 6a88139..631781e 100644 --- a/tests/unit/test_est.c +++ b/tests/unit/test_est.c @@ -305,6 +305,10 @@ static int test_est_require_server_auth(void) REQUIRE(wolfcert_est_simple_reenroll(&srv, dummy_csr, sizeof(dummy_csr), rk, dummy_csr, sizeof(dummy_csr), &res) == WOLFCERT_ERR_TLS); + /* An empty current cert is refused before the config is checked. */ + REQUIRE(wolfcert_est_simple_reenroll(&srv, dummy_csr, 0, rk, dummy_csr, + sizeof(dummy_csr), &res) + == WOLFCERT_ERR_BAD_ARG); wolfcert_key_free(rk); REQUIRE(wolfcert_est_session_open(&srv, &sess) == WOLFCERT_ERR_TLS); @@ -329,7 +333,7 @@ static int test_est_require_server_auth(void) return 0; } -/* A SCEP config gets BAD_ARG from the EST one-shots and both session opens. */ +/* A SCEP config gets BAD_ARG from the EST one-shots and the session open. */ static int test_est_rejects_scep_cfg(void) { static const uint8_t dummy_csr[] = { 0x30, 0x03, 0x02, 0x01, 0x00 }; @@ -363,8 +367,6 @@ static int test_est_rejects_scep_cfg(void) REQUIRE(wolfcert_est_session_open(&srv, &sess) == WOLFCERT_ERR_BAD_ARG); REQUIRE(sess == NULL); - REQUIRE(wolfcert_est_session_open_async(&srv, &sess) == WOLFCERT_ERR_BAD_ARG); - REQUIRE(sess == NULL); /* protocol = 0 is refused too. */ srv.protocol = (WolfCertProtocol)0; @@ -710,7 +712,7 @@ int main(void) /* Non-blocking session enroll also sends the base64 CTE header. */ WolfCertEstSession* sess_nb = NULL; - REQUIRE(wolfcert_est_session_open_async(&srv, &sess_nb) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_open(&srv, &sess_nb) == WOLFCERT_OK); WolfCertEstResult sess_nb_enrolled = { 0 }; REQUIRE(pump_simple_enroll(sess_nb, csr.data, csr.len, &sess_nb_enrolled) == 0); diff --git a/wolfcert/est.h b/wolfcert/est.h index 043e80e..73c351a 100644 --- a/wolfcert/est.h +++ b/wolfcert/est.h @@ -32,6 +32,12 @@ extern "C" { WOLFCERT_API int wolfcert_est_get_cacerts(const WolfCertServerCfg* srv, WolfCertBuffer* out_ca_pem); +/* wolfcert_est_get_cacerts in the requested encoding; DER gives the chain as + * concatenated DER. */ +WOLFCERT_API int wolfcert_est_get_cacerts_enc(const WolfCertServerCfg* srv, + WolfCertEncoding enc, + WolfCertBuffer* out_ca); + /* GET /.well-known/est/csrattrs - returns the CsrAttrs DER for * wolfcert_est_parse_csr_attrs, empty (NULL, 0) on 204, 404, 405, 501 or a * 200 with no body. A 401 or 403 returns WOLFCERT_ERR_AUTH, others _HTTP. */ @@ -130,7 +136,8 @@ typedef struct { /* Every wolfcert_est_* call taking a WolfCertEstResult* initializes a * non-NULL *out first, so freeing it after any outcome is safe. A populated - * result must be freed before it is passed again. */ + * result must be freed before it is passed again, except a session's + * in-flight result, which every resume passes back unfreed. */ WOLFCERT_API void wolfcert_est_result_free(WolfCertEstResult* r); /* POST /.well-known/est/simpleenroll. */ @@ -187,15 +194,11 @@ WOLFCERT_API void wolfcert_est_session_close(WolfCertEstSession* s); * -1 when a caller-supplied WolfCertTransport backs it (no descriptor). */ WOLFCERT_API int wolfcert_est_session_fd(const WolfCertEstSession* s); -/* Non-blocking variants, for a session from wolfcert_est_session_open or - * wolfcert_est_session_open_async. Each _nb call returns WOLFCERT_OK once the - * output is populated, or WOLFCERT_ERR_WANT_READ / _WANT_WRITE to be repeated - * with the same arguments when the fd is ready; any other value ends the - * request. While a _nb request is in flight, any other request on the session - * returns WOLFCERT_ERR_BAD_ARG and leaves it intact. */ -WOLFCERT_API int wolfcert_est_session_open_async(const WolfCertServerCfg* srv, - WolfCertEstSession** out); - +/* Non-blocking variants. Each _nb call returns WOLFCERT_OK once the output is + * populated, or WOLFCERT_ERR_WANT_READ / _WANT_WRITE to be repeated with the + * same arguments when the fd is ready; any other value ends the request. While + * a _nb request is in flight, any other request on the session returns + * WOLFCERT_ERR_BAD_ARG and leaves it intact. */ WOLFCERT_API int wolfcert_est_session_get_cacerts_nb(WolfCertEstSession* s, WolfCertBuffer* out_ca_pem); From 92e50cbd8b38954e4e677512077ba4daa0c98ed7 Mon Sep 17 00:00:00 2001 From: Yosuke Shimizu Date: Mon, 19 Oct 2026 20:35:16 +0900 Subject: [PATCH 4/5] scep: one session call, one PKIOperation builder, one session type - scep_session_call() runs every session call from a ScepCall, and scep_one_shot() every one-shot; run_pki_op(), do_scep_round_trip() and fill_common() are removed. - scep_session_begin_pki_op() builds every PKIOperation; scep_prepare() and scep_finish() read the request state from the session. - New session calls with _nb forms: get_ca_caps, get_ca_cert_enc, get_next_ca_cert and get_cert. Every call accepts a NULL caps. - wolfcert_scep_session_open_async() and the mode checks are removed; a blocking call while a request is in flight gets WOLFCERT_ERR_BAD_ARG, on EST and SCEP sessions alike. - A non-PrintableString GetCertInitial transactionID gets WOLFCERT_ERR_BAD_ARG before the dial. - scep_one_shot() sends Connection: close. - scep_session_free_request() frees the request URL and pkiMessage once the HTTP call succeeds, before scep_session_finish(). - scep_call_clear() also clears a buffer output. - scep_one_shot() builds the request before it connects: session open is split into scep_session_new() and scep_session_connect(), and the send step into scep_session_send(). - The SCEP session hands its URL to the HTTP session, and wolfcert_http_url_origin() is removed. - A blocking SCEP call uses the caller's CA bundle and signer cert; an _nb request keeps owned copies. - rsa_key_to_der() allocates the exact DER size, and scep_session_begin_pki_op() converts the key just before it signs the pkiMessage. --- src/http.c | 32 - src/internal.h | 2 - src/scep/scep_client.c | 2288 ++++++++--------- .../integration/test_est_pending_roundtrip.c | 8 + tests/integration/test_scep_async_roundtrip.c | 229 +- tests/integration/test_scep_roundtrip.c | 4 + tests/unit/test_http.c | 58 - tests/unit/test_scep_msg.c | 41 +- wolfcert/scep.h | 52 +- 9 files changed, 1282 insertions(+), 1432 deletions(-) diff --git a/src/http.c b/src/http.c index 6519cf3..89fbc1f 100644 --- a/src/http.c +++ b/src/http.c @@ -130,38 +130,6 @@ static int host_is_ip_literal(const char* host) return wolfcert_parse_ip(host, ip, &ip_len) == WOLFCERT_OK && ip_len == 16; } -/* "scheme://host[:port]" without a default port; free with WOLFCERT_XFREE. */ -WOLFCERT_TEST_VIS int wolfcert_http_url_origin(const WolfCertUrl* u, void* heap, - char** out_origin) -{ - size_t origin_len; - char* origin; - const char* open_br; - const char* close_br; - - if (u == NULL || u->scheme == NULL || u->host == NULL || out_origin == NULL) - return WOLFCERT_ERR_BAD_ARG; - - open_br = host_is_ip_literal(u->host) ? "[" : ""; - close_br = host_is_ip_literal(u->host) ? "]" : ""; - - /* "://", then room for the brackets, ":65535" and the NUL. */ - 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%s%s", u->scheme, - open_br, u->host, close_br); - else - snprintf(origin, origin_len, "%s://%s%s%s:%d", u->scheme, - open_br, u->host, close_br, 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); diff --git a/src/internal.h b/src/internal.h index bac5300..9911c1d 100644 --- a/src/internal.h +++ b/src/internal.h @@ -273,8 +273,6 @@ 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); /* Resume point for wolfcert_http_chunked_complete(); zero it before the first * call, setting max to cap the decoded body (0 for no cap). */ diff --git a/src/scep/scep_client.c b/src/scep/scep_client.c index 2e791e6..76b39f9 100644 --- a/src/scep/scep_client.c +++ b/src/scep/scep_client.c @@ -17,10 +17,6 @@ * along with wolfCert. If not, see . */ -#define _POSIX_C_SOURCE 200809L -#define _DEFAULT_SOURCE -#define _GNU_SOURCE - #include #include #include @@ -55,23 +51,6 @@ static char* append_query(const char* base, const char* op, void* heap) return url; } -/* RFC 8894 defines no HTTP-layer authentication, so no Basic credentials. */ -static void fill_common(const WolfCertServerCfg* srv, WolfCertHttpRequest* req) -{ - req->trust_anchors = srv->trust_anchors; - req->trust_anchors_len = srv->trust_anchors_len; - req->verify_server = srv->verify_server; - req->timeout_ms = srv->timeout_ms; - req->max_response_bytes = srv->max_response_bytes; - req->heap = srv->heap; - - req->client_cert = srv->client_cert; - req->client_cert_len = srv->client_cert_len; - req->client_key = srv->client_key; - req->client_key_len = srv->client_key_len; - req->transport = srv->transport; -} - /* RFC 8894 section 3.5.2: each GetCACaps line is one whole capability token. */ static int has_cap(const char* body, size_t len, const char* needle) { @@ -104,84 +83,6 @@ static int has_cap(const char* body, size_t len, const char* needle) return 0; } -/* The protocol check must precede every read of proto_opts.scep. Plain - * http:// is allowed, but an https:// endpoint requires verify_server. */ -static int scep_check_cfg(const WolfCertServerCfg* srv, void* heap) -{ - WolfCertUrl u; - int rc = wolfcert_cfg_require_proto(srv, WOLFCERT_PROTO_SCEP, "scep"); - if (rc != WOLFCERT_OK) - return rc; - - rc = wolfcert_http_url_parse(srv->server_url, &u, heap); - if (rc != WOLFCERT_OK) - return rc; - - int tls = u.tls; - wolfcert_http_url_free(&u); - - if (tls && !srv->verify_server) - return WOLFCERT_ERR(WOLFCERT_ERR_TLS, "scep", - "TLS SCEP endpoint requires server authentication: set verify_server " - "or use a plaintext http:// URL"); - - return WOLFCERT_OK; -} - -int wolfcert_scep_get_ca_caps(const WolfCertServerCfg* srv, WolfCertScepCaps* out) -{ - if (srv == NULL || srv->server_url == NULL || out == NULL) - return WOLFCERT_ERR_BAD_ARG; - - void* heap = srv->heap ? srv->heap : wolfcert_default_heap(); - int trc = scep_check_cfg(srv, heap); - if (trc != WOLFCERT_OK) - return trc; - - memset(out, 0, sizeof(*out)); - - char* url = wolfcert_scep_build_getca_url(srv->server_url, "GetCACaps", - srv->proto_opts.scep.ca_id, heap); - if (url == NULL) - return WOLFCERT_ERR_MEMORY; - - WolfCertHttpRequest req = { .method = "GET", .url = url }; - fill_common(srv, &req); - - WolfCertHttpResponse resp = { 0 }; - int rc = wolfcert_http_request(&req, &resp); - - WOLFCERT_XFREE(url, heap); - if (rc != WOLFCERT_OK) - return rc; - - if (resp.status_code != 200) { - wolfcert_http_response_free(&resp); - return WOLFCERT_ERR_HTTP; - } - - const char* b = (const char*)resp.body; - out->post_pki_operation = has_cap(b, resp.body_len, "POSTPKIOperation"); - out->renewal = has_cap(b, resp.body_len, "Renewal"); - out->sha256 = has_cap(b, resp.body_len, "SHA-256"); - out->sha384 = has_cap(b, resp.body_len, "SHA-384"); - out->sha512 = has_cap(b, resp.body_len, "SHA-512"); - out->aes = has_cap(b, resp.body_len, "AES"); - out->scep_standard = has_cap(b, resp.body_len, "SCEPStandard"); - out->get_next_ca_cert = has_cap(b, resp.body_len, "GetNextCACert"); - - /* RFC 8894 section 3.5.2, Table 7 */ - if (out->scep_standard) { - out->post_pki_operation = 1; - out->aes = 1; - out->sha256 = 1; - } - - wolfcert_http_response_free(&resp); - - return WOLFCERT_OK; -} - void wolfcert_scep_result_free(WolfCertScepResult* r) { if (r == NULL) @@ -195,110 +96,6 @@ void wolfcert_scep_result_free(WolfCertScepResult* r) r->fail_info = -1; } -int wolfcert_scep_get_ca_cert(const WolfCertServerCfg* srv, WolfCertBuffer* out_ca_pem) -{ - return wolfcert_scep_get_ca_cert_enc(srv, WOLFCERT_ENCODING_PEM, out_ca_pem); -} - -int wolfcert_scep_get_ca_cert_enc(const WolfCertServerCfg* srv, WolfCertEncoding enc, - WolfCertBuffer* out_ca) -{ - if (srv == NULL || srv->server_url == NULL || out_ca == NULL) - return WOLFCERT_ERR_BAD_ARG; - - void* heap = srv->heap ? srv->heap : wolfcert_default_heap(); - int trc = scep_check_cfg(srv, heap); - if (trc != WOLFCERT_OK) - return trc; - - - char* url = wolfcert_scep_build_getca_url(srv->server_url, "GetCACert", - srv->proto_opts.scep.ca_id, heap); - if (url == NULL) - return WOLFCERT_ERR_MEMORY; - - WolfCertHttpRequest req = { .method = "GET", .url = url }; - fill_common(srv, &req); - - WolfCertHttpResponse resp = { 0 }; - int rc = wolfcert_http_request(&req, &resp); - - WOLFCERT_XFREE(url, heap); - if (rc != WOLFCERT_OK) - return rc; - - if (resp.status_code != 200) { - wolfcert_http_response_free(&resp); - return WOLFCERT_ERR_HTTP; - } - - if (resp.body_len == 0) { - wolfcert_http_response_free(&resp); - return WOLFCERT_ERR(WOLFCERT_ERR_HTTP, "scep", - "GetCACert: 200 response carried no certificate"); - } - - /* Media types compare case-insensitively; ';' parameters are ignored. */ - static const char ca_ra_type[] = "application/x-x509-ca-ra-cert"; - int is_p7 = 0; - if (resp.content_type != NULL && - wolfcert_ascii_ncasecmp(resp.content_type, ca_ra_type, - sizeof(ca_ra_type) - 1) == 0) { - const char* p = resp.content_type + sizeof(ca_ra_type) - 1; - while (*p == ' ' || *p == '\t') - ++p; - is_p7 = (*p == '\0' || *p == ';'); - } - if (!is_p7) - is_p7 = wolfcert_scep_is_signed_data(resp.body, resp.body_len); - if (is_p7) { - if (enc == WOLFCERT_ENCODING_DER) { - rc = wolfcert_pkcs7_certs_to_der(resp.body, resp.body_len, out_ca, heap); - } - else { - rc = wolfcert_pkcs7_certs_to_pem(resp.body, resp.body_len, out_ca, heap); - } - } - else if (enc == WOLFCERT_ENCODING_DER) { - /* A single CA cert arrives as raw DER. */ - uint8_t* der = (uint8_t*)WOLFCERT_XMALLOC(resp.body_len, heap); - if (der == NULL) { - rc = WOLFCERT_ERR_MEMORY; - goto out; - } - - memcpy(der, resp.body, resp.body_len); - out_ca->data = der; - out_ca->len = resp.body_len; - out_ca->heap = heap; - rc = WOLFCERT_OK; - } - else { - size_t cap = resp.body_len * 2 + 256; - uint8_t* pem = (uint8_t*)WOLFCERT_XMALLOC(cap, heap); - if (pem == NULL) { - rc = WOLFCERT_ERR_MEMORY; - goto out; - } - - int n = wc_DerToPem(resp.body, (word32)resp.body_len, pem, (word32)cap, CERT_TYPE); - if (n <= 0) { - WOLFCERT_XFREE(pem, heap); - rc = WOLFCERT_ERR_CRYPTO; - goto out; - } - - out_ca->data = pem; - out_ca->len = (size_t)n; - out_ca->heap = heap; - rc = WOLFCERT_OK; - } - -out: - wolfcert_http_response_free(&resp); - return rc; -} - int wolfcert_scep_verify_ca_fingerprint(const uint8_t* ca_der, size_t ca_der_len, const uint8_t* expected, size_t expected_len, WolfCertScepFpAlg alg) @@ -522,48 +319,6 @@ static int scep_build_transport(const char* server_url, const WolfCertScepCaps* out_url); } -static int run_pki_op(const WolfCertServerCfg* srv, - const WolfCertScepCaps* caps, - const uint8_t* pki_msg, size_t pki_len, - uint8_t** out_resp, size_t* out_resp_len) -{ - void* heap = srv->heap ? srv->heap : wolfcert_default_heap(); - - char* url = NULL; - int use_post = 0; - int rc = scep_build_transport(srv->server_url, caps, pki_msg, pki_len, heap, - &url, &use_post); - if (rc != WOLFCERT_OK) - return rc; - - WolfCertHttpRequest req = { .method = use_post ? "POST" : "GET", .url = url }; - if (use_post) { - req.content_type = "application/x-pki-message"; - req.body = pki_msg; - req.body_len = pki_len; - } - - fill_common(srv, &req); - WolfCertHttpResponse resp = { 0 }; - rc = wolfcert_http_request(&req, &resp); - - WOLFCERT_XFREE(url, heap); - if (rc != WOLFCERT_OK) - return rc; - - if (resp.status_code != 200) { - wolfcert_http_response_free(&resp); - return WOLFCERT_ERR_HTTP; - } - - *out_resp = resp.body; - *out_resp_len = resp.body_len; - resp.body = NULL; - wolfcert_http_response_free(&resp); - - return WOLFCERT_OK; -} - /* PKCSReq (19) serves CAs that predate RenewalReq (17); either way the signer * is the cert being replaced. */ static const char* scep_renewal_msg_type(WolfCertScepRenewalMsgType m) @@ -658,509 +413,49 @@ static int scep_build_txid(const ScepTxidSel* sel, return WOLFCERT_OK; } -/* Build the pkiMessage; the caller keeps *out_txid and out_nonce to validate - * the CertRep. */ -static int scep_prepare(void* heap, const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* signer_cert, size_t signer_cert_len, - const uint8_t* signer_key, size_t signer_key_len, - const char* msg_type, - const uint8_t* envelope_content, size_t envelope_content_len, - const ScepTxidSel* txid_sel, - WolfCertScepContentCipher cipher, - WolfCertScepSigningHash hash, - WolfCertBuffer* out_pki, - uint8_t** out_txid, size_t* out_txid_len, - uint8_t* out_nonce) +/* The caller has already checked that key->type is WOLFCERT_KEY_RSA. */ +static int rsa_key_to_der(const WolfCertKey* key, void* heap, + uint8_t** out_der, size_t* out_len) { - int hash_oid = wolfcert_scep_pick_hash_oid(caps, hash); - int enc_oid; - - if (hash_oid < 0) - return hash_oid; - - /* AUTO follows RFC 8894: GetCACaps "AES" means AES-128-CBC, else 3DES. */ - switch (cipher) { - case WOLFCERT_SCEP_CIPHER_AES128: - enc_oid = AES128CBCb; - break; - case WOLFCERT_SCEP_CIPHER_AES256: -#if !defined(WOLFSSL_AES_256) || !defined(HAVE_AES_CBC) - return WOLFCERT_ERR(WOLFCERT_ERR_UNSUPPORTED, "scep", - "AES-256-CBC content cipher requested but wolfSSL lacks it"); -#else - enc_oid = AES256CBCb; - break; -#endif - case WOLFCERT_SCEP_CIPHER_DES3: -#ifdef NO_DES3 - return WOLFCERT_ERR(WOLFCERT_ERR_UNSUPPORTED, "scep", - "3DES content cipher requested but wolfSSL was built NO_DES3"); -#else - enc_oid = DES3b; - break; -#endif - case WOLFCERT_SCEP_CIPHER_AUTO: - default: - if (caps != NULL && caps->aes) { - enc_oid = AES128CBCb; - } - else { -#ifdef NO_DES3 - return WOLFCERT_ERR(WOLFCERT_ERR_UNSUPPORTED, "scep", - "no usable content cipher: CA does not advertise AES and " - "wolfSSL lacks the 3DES fallback"); -#else - enc_oid = DES3b; -#endif - } - break; - } - - WolfCertBuffer env = { 0 }; - int rc = wolfcert_scep_envelop(ra_cert, ra_cert_len, - envelope_content, envelope_content_len, - enc_oid, &env, heap); - if (rc != WOLFCERT_OK) - return rc; - - WC_RNG rng; - int rng_rc = wc_InitRng_ex(&rng, heap, WOLFCERT_DEVID_SOFTWARE); - if (rng_rc != 0) { - wolfcert_buffer_free(&env); - return WOLFCERT_ERR_WC(rng_rc, "scep", "RNG init failed"); - } - - rng_rc = wc_RNG_GenerateBlock(&rng, out_nonce, SCEP_NONCE_SZ); - if (rng_rc != 0) { - wc_ForceZero(out_nonce, SCEP_NONCE_SZ); - wc_FreeRng(&rng); - wolfcert_buffer_free(&env); - return WOLFCERT_ERR_WC(rng_rc, "scep", "RNG failed generating senderNonce"); - } + uint8_t* der; + int sz; + int n; - uint8_t* txid = NULL; - size_t txid_len = 0; - rc = scep_build_txid(txid_sel, signer_cert, signer_cert_len, &rng, heap, - &txid, &txid_len); - wc_FreeRng(&rng); - if (rc != WOLFCERT_OK) { - wc_ForceZero(out_nonce, SCEP_NONCE_SZ); - wolfcert_buffer_free(&env); - return rc; - } + sz = wc_RsaKeyToDer((RsaKey*)key->impl, NULL, 0); + if (sz <= 0) + return WOLFCERT_ERR_WC(sz, "scep", "RsaKeyToDer"); - WolfCertScepAttrs attrs = { - .transaction_id = txid, .transaction_id_len = txid_len, - .sender_nonce = out_nonce, .sender_nonce_len = SCEP_NONCE_SZ, - .message_type = msg_type, - }; - rc = wolfcert_scep_build_pki_message(env.data, env.len, - signer_cert, signer_cert_len, - signer_key, signer_key_len, - hash_oid, &attrs, out_pki, heap); + der = (uint8_t*)WOLFCERT_XMALLOC((size_t)sz, heap); + if (der == NULL) + return WOLFCERT_ERR_MEMORY; - wolfcert_buffer_free(&env); - if (rc != WOLFCERT_OK) { - WOLFCERT_XFREE(txid, heap); - return rc; + n = wc_RsaKeyToDer((RsaKey*)key->impl, der, (word32)sz); + if (n <= 0) { + wc_ForceZero(der, (word32)sz); + WOLFCERT_XFREE(der, heap); + return WOLFCERT_ERR_WC(n, "scep", "RsaKeyToDer"); } - *out_txid = txid; - *out_txid_len = txid_len; + *out_der = der; + *out_len = (size_t)n; return WOLFCERT_OK; } -/* Validate a CertRep against the request and fill `out`; reads but does not - * free `resp`. */ -static int scep_finish(void* heap, - const uint8_t* resp, size_t resp_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const uint8_t* signer_cert, size_t signer_cert_len, - const uint8_t* signer_key, size_t signer_key_len, - const uint8_t* txid, size_t txid_len, - const uint8_t* nonce, - WolfCertScepResult* out) +WOLFCERT_TEST_VIS int wolfcert_scep_pem_has_cert(const uint8_t* pem, size_t pem_len, + const uint8_t* issuer, size_t issuer_len, + const uint8_t* serial, size_t serial_len, + void* heap) { - WolfCertBuffer resp_env = { 0 }; - WolfCertBuffer inner = { 0 }; - char* status = NULL; - char* fail_info = NULL; - char* resp_mt = NULL; - uint8_t* rx_tid = NULL; - size_t rx_tid_len = 0; - uint8_t* rx_sn = NULL; - size_t rx_sn_len = 0; - uint8_t* rx_rn = NULL; - size_t rx_rn_len = 0; - uint8_t* rx_signer = NULL; - size_t rx_signer_len = 0; - int rc = wolfcert_scep_parse_pki_message(resp, resp_len, &resp_env, - &rx_tid, &rx_tid_len, &rx_sn, &rx_sn_len, &rx_rn, &rx_rn_len, - &resp_mt, &status, &rx_signer, &rx_signer_len, &fail_info, heap); - - WOLFCERT_XFREE(rx_sn, heap); + static const char BEGIN[] = "-----BEGIN CERTIFICATE-----"; + const size_t blen = sizeof(BEGIN) - 1; + DecodedCert* dc; + const char* p; + const char* end; + int match = 0; - /* RFC 8894: a CertRep is messageType 3 and echoes our transactionID. */ - if (rc == WOLFCERT_OK) { - rc = wolfcert_scep_check_cert_rep(resp_mt, rx_tid, rx_tid_len, - txid, txid_len); - if (rc != WOLFCERT_OK) - rc = WOLFCERT_ERR(WOLFCERT_ERR_PROTOCOL, "scep", - "CertRep messageType or transactionID does not " - "match the request"); - } - WOLFCERT_XFREE(resp_mt, heap); - - /* Authenticate the CertRep: its signer's key must match a cert in the - * GetCACert bundle. */ - if (rc == WOLFCERT_OK) { - rc = wolfcert_scep_verify_rep_signer(rx_signer, rx_signer_len, - ca_bundle, ca_bundle_len, heap); - if (rc == WOLFCERT_ERR_MEMORY) - rc = WOLFCERT_ERR(rc, "scep", "out of memory checking the CertRep signer"); - else if (rc != WOLFCERT_OK) - rc = WOLFCERT_ERR(WOLFCERT_ERR_AUTH, "scep", - "CertRep is not signed by the CA/RA certificate"); - } - WOLFCERT_XFREE(rx_signer, heap); - - /* RFC 8894 section 3.2.1.5: recipientNonce must echo our senderNonce. */ - if (rc == WOLFCERT_OK && - (rx_rn == NULL || rx_rn_len != SCEP_NONCE_SZ || - memcmp(rx_rn, nonce, SCEP_NONCE_SZ) != 0)) { - rc = WOLFCERT_ERR(WOLFCERT_ERR_PROTOCOL, "scep", - "CertRep recipientNonce missing or does not echo " - "senderNonce"); - } - WOLFCERT_XFREE(rx_rn, heap); - - /* RFC 8894 section 3.2.1.3: a CertRep carries pkiStatus 0, 2 or 3. */ - if (rc == WOLFCERT_OK && - (status == NULL || (strcmp(status, "0") != 0 && - strcmp(status, "2") != 0 && - strcmp(status, "3") != 0))) { - rc = WOLFCERT_ERR(WOLFCERT_ERR_PROTOCOL, "scep", - "CertRep pkiStatus missing or not 0, 2 or 3"); - } - - /* RFC 8894 section 3.2.1.4: a FAILURE CertRep carries failInfo 0..4. */ - if (rc == WOLFCERT_OK && strcmp(status, "2") == 0 && - (fail_info == NULL || fail_info[0] < '0' || fail_info[0] > '4' || - fail_info[1] != '\0')) { - rc = WOLFCERT_ERR(WOLFCERT_ERR_PROTOCOL, "scep", - "FAILURE CertRep failInfo missing or not 0..4"); - } - - if (rc == WOLFCERT_OK) { - /* Ownership of rx_tid moves to out. */ - out->transaction_id = rx_tid; - out->transaction_id_len = rx_tid_len; - rx_tid = NULL; - - if (strcmp(status, "3") == 0) { - out->status = WOLFCERT_SCEP_STATUS_PENDING; - } - else if (strcmp(status, "2") == 0) { - out->status = WOLFCERT_SCEP_STATUS_FAILURE; - out->fail_info = fail_info[0] - '0'; - } - else { - rc = wolfcert_scep_deenvelop(signer_cert, signer_cert_len, - signer_key, signer_key_len, - resp_env.data, resp_env.len, &inner, - heap); - if (rc == WOLFCERT_OK) - rc = wolfcert_pkcs7_certs_to_pem(inner.data, inner.len, - &out->cert_pem, heap); - if (rc == WOLFCERT_OK) - out->status = WOLFCERT_SCEP_STATUS_SUCCESS; - } - } - - WOLFCERT_XFREE(rx_tid, heap); - WOLFCERT_XFREE(status, heap); - WOLFCERT_XFREE(fail_info, heap); - wolfcert_buffer_free(&inner); - wolfcert_buffer_free(&resp_env); - return rc; -} - -/* A NULL `txid_override` derives the transactionID per txid_mode. */ -static int do_scep_round_trip(const WolfCertServerCfg* srv, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const uint8_t* signer_cert, size_t signer_cert_len, - const uint8_t* signer_key, size_t signer_key_len, - const char* msg_type, - const uint8_t* envelope_content, size_t envelope_content_len, - const uint8_t* txid_override, size_t txid_override_len, - WolfCertScepResult* out) -{ - void* heap = srv->heap ? srv->heap : wolfcert_default_heap(); - out->heap = heap; - out->fail_info = -1; - - ScepTxidSel txid_sel = { - .id = txid_override, .id_len = txid_override_len, - .mode = srv->proto_opts.scep.txid_mode - }; - - WolfCertBuffer pki = { 0 }; - uint8_t* txid = NULL; - size_t txid_len = 0; - uint8_t nonce[SCEP_NONCE_SZ]; - int rc = scep_prepare(heap, caps, ra_cert, ra_cert_len, - signer_cert, signer_cert_len, signer_key, signer_key_len, - msg_type, envelope_content, envelope_content_len, - &txid_sel, srv->proto_opts.scep.content_cipher, - srv->proto_opts.scep.signing_hash, - &pki, &txid, &txid_len, nonce); - if (rc != WOLFCERT_OK) - return rc; - - uint8_t* resp = NULL; - size_t resp_len = 0; - rc = run_pki_op(srv, caps, pki.data, pki.len, &resp, &resp_len); - wolfcert_buffer_free(&pki); - - if (rc == WOLFCERT_OK) { - rc = scep_finish(heap, resp, resp_len, ca_bundle, ca_bundle_len, - signer_cert, signer_cert_len, signer_key, signer_key_len, - txid, txid_len, nonce, out); - WOLFCERT_XFREE(resp, heap); - } - - wc_ForceZero(nonce, (word32)sizeof(nonce)); - WOLFCERT_XFREE(txid, heap); - - return rc; -} - -/* The caller has already checked that key->type is WOLFCERT_KEY_RSA. */ -static int rsa_key_to_der(const WolfCertKey* key, void* heap, - uint8_t** out_der, size_t* out_len) -{ - size_t bits = key->rsa_bits ? (size_t)key->rsa_bits : 4096; - size_t cap = bits + 2048; - uint8_t* der = (uint8_t*)WOLFCERT_XMALLOC(cap, heap); - int n; - - if (der == NULL) - return WOLFCERT_ERR_MEMORY; - - n = wc_RsaKeyToDer((RsaKey*)key->impl, der, (word32)cap); - if (n <= 0) { - wc_ForceZero(der, (word32)cap); - WOLFCERT_XFREE(der, heap); - return WOLFCERT_ERR_WC(n, "scep", "RsaKeyToDer"); - } - - *out_der = der; - *out_len = (size_t)n; - - return WOLFCERT_OK; -} - -int wolfcert_scep_pkcs_req(const WolfCertServerCfg* srv, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const WolfCertKey* new_key, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertScepResult* out) -{ - if (out == NULL) - return WOLFCERT_ERR_BAD_ARG; - - memset(out, 0, sizeof(*out)); - out->fail_info = -1; - - if (srv == NULL || ra_cert == NULL || ra_cert_len == 0 || - ca_bundle == NULL || ca_bundle_len == 0 || new_key == NULL || - csr_der == NULL || csr_der_len == 0) - return WOLFCERT_ERR_BAD_ARG; - - if (new_key->type != WOLFCERT_KEY_RSA) - return WOLFCERT_ERR(WOLFCERT_ERR_UNSUPPORTED, "scep", - "SCEP (RFC 8894) requires an RSA signer for pkiMessage; " - "Ed25519/Ed448/ML-DSA are not permitted"); - void* heap = srv->heap ? srv->heap : wolfcert_default_heap(); - int trc = scep_check_cfg(srv, heap); - if (trc != WOLFCERT_OK) - return trc; - - uint8_t* signer_der = NULL; - size_t signer_len = 0; - int rc = wolfcert_scep_self_signed_rsa((RsaKey*)new_key->impl, - csr_der, csr_der_len, - &signer_der, &signer_len, heap); - if (rc != WOLFCERT_OK) - return rc; - - uint8_t* key_der = NULL; - size_t key_der_len = 0; - rc = rsa_key_to_der(new_key, heap, &key_der, &key_der_len); - if (rc != WOLFCERT_OK) { - WOLFCERT_XFREE(signer_der, heap); - return rc; - } - - rc = do_scep_round_trip(srv, caps, ra_cert, ra_cert_len, - ca_bundle, ca_bundle_len, - signer_der, signer_len, key_der, key_der_len, - "19", csr_der, csr_der_len, - NULL, 0, out); - - WOLFCERT_XFREE(signer_der, heap); - wc_ForceZero(key_der, (word32)key_der_len); - WOLFCERT_XFREE(key_der, heap); - return rc; -} - -int wolfcert_scep_renewal_req(const WolfCertServerCfg* srv, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const uint8_t* current_cert, size_t current_cert_len, - const WolfCertKey* current_key, - const uint8_t* csr_der, size_t csr_der_len, - WolfCertScepResult* out) -{ - if (out == NULL) - return WOLFCERT_ERR_BAD_ARG; - - memset(out, 0, sizeof(*out)); - out->fail_info = -1; - - if (srv == NULL || ra_cert == NULL || ra_cert_len == 0 || - ca_bundle == NULL || ca_bundle_len == 0 || - current_cert == NULL || current_cert_len == 0 || - current_key == NULL || csr_der == NULL || csr_der_len == 0) - return WOLFCERT_ERR_BAD_ARG; - - if (current_key->type != WOLFCERT_KEY_RSA) - return WOLFCERT_ERR(WOLFCERT_ERR_UNSUPPORTED, "scep", - "SCEP (RFC 8894) requires an RSA signer for pkiMessage; " - "Ed25519/Ed448/ML-DSA are not permitted"); - void* heap = srv->heap ? srv->heap : wolfcert_default_heap(); - int trc = scep_check_cfg(srv, heap); - if (trc != WOLFCERT_OK) - return trc; - - uint8_t* key_der = NULL; - size_t key_der_len = 0; - int rc = rsa_key_to_der(current_key, heap, &key_der, &key_der_len); - if (rc != WOLFCERT_OK) - return rc; - - rc = do_scep_round_trip(srv, caps, ra_cert, ra_cert_len, - ca_bundle, ca_bundle_len, - current_cert, current_cert_len, - key_der, key_der_len, - scep_renewal_msg_type( - srv->proto_opts.scep.renewal_msg_type), - csr_der, csr_der_len, - NULL, 0, out); - - wc_ForceZero(key_der, (word32)key_der_len); - WOLFCERT_XFREE(key_der, heap); - return rc; -} - -int wolfcert_scep_get_cert_initial(const WolfCertServerCfg* srv, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const uint8_t* signer_cert, size_t signer_cert_len, - const WolfCertKey* signer_key, - const uint8_t* csr_der, size_t csr_der_len, - const uint8_t* transaction_id, - size_t transaction_id_len, - WolfCertScepResult* out) -{ - if (out == NULL) - return WOLFCERT_ERR_BAD_ARG; - - memset(out, 0, sizeof(*out)); - out->fail_info = -1; - - if (srv == NULL || ra_cert == NULL || ra_cert_len == 0 || - ca_bundle == NULL || ca_bundle_len == 0 || - (signer_cert != NULL && signer_cert_len == 0) || - signer_key == NULL || csr_der == NULL || csr_der_len == 0 || - transaction_id == NULL || transaction_id_len == 0) - return WOLFCERT_ERR_BAD_ARG; - - if (signer_key->type != WOLFCERT_KEY_RSA) - return WOLFCERT_ERR(WOLFCERT_ERR_UNSUPPORTED, "scep", - "SCEP (RFC 8894) requires an RSA signer for pkiMessage"); - void* heap = srv->heap ? srv->heap : wolfcert_default_heap(); - int trc = scep_check_cfg(srv, heap); - if (trc != WOLFCERT_OK) - return trc; - - - WolfCertBuffer ias = { 0 }; - uint8_t* key_der = NULL; - size_t key_der_len = 0; - uint8_t* derived_signer = NULL; - size_t derived_signer_len = 0; - const uint8_t* eff_signer = signer_cert; - size_t eff_signer_len = signer_cert_len; - - int rc = wolfcert_scep_issuer_and_subject(ra_cert, ra_cert_len, - csr_der, csr_der_len, &ias, heap); - - if (rc == WOLFCERT_OK) - rc = rsa_key_to_der(signer_key, heap, &key_der, &key_der_len); - - /* A pending PKCSReq has no signer cert; rebuild the transient - * self-signed one from signer_key. */ - if (rc == WOLFCERT_OK && signer_cert == NULL) { - rc = wolfcert_scep_self_signed_rsa((RsaKey*)signer_key->impl, - csr_der, csr_der_len, - &derived_signer, &derived_signer_len, - heap); - if (rc == WOLFCERT_OK) { - eff_signer = derived_signer; - eff_signer_len = derived_signer_len; - } - } - - if (rc == WOLFCERT_OK) - rc = do_scep_round_trip(srv, caps, ra_cert, ra_cert_len, - ca_bundle, ca_bundle_len, - eff_signer, eff_signer_len, - key_der, key_der_len, - "20", ias.data, ias.len, - transaction_id, transaction_id_len, out); - - wolfcert_buffer_free(&ias); - if (key_der != NULL) { - wc_ForceZero(key_der, (word32)key_der_len); - WOLFCERT_XFREE(key_der, heap); - } - WOLFCERT_XFREE(derived_signer, heap); - return rc; -} - -WOLFCERT_TEST_VIS int wolfcert_scep_pem_has_cert(const uint8_t* pem, size_t pem_len, - const uint8_t* issuer, size_t issuer_len, - const uint8_t* serial, size_t serial_len, - void* heap) -{ - static const char BEGIN[] = "-----BEGIN CERTIFICATE-----"; - const size_t blen = sizeof(BEGIN) - 1; - DecodedCert* dc; - const char* p; - const char* end; - int match = 0; - - if (pem == NULL || issuer == NULL || serial == NULL || pem_len < blen) - return 0; + if (pem == NULL || issuer == NULL || serial == NULL || pem_len < blen) + return 0; dc = (DecodedCert*)WOLFCERT_XMALLOC(sizeof(*dc), heap); if (dc == NULL) @@ -1213,164 +508,214 @@ WOLFCERT_TEST_VIS int wolfcert_scep_pem_has_cert(const uint8_t* pem, size_t pem_ return match; } -int wolfcert_scep_get_cert(const WolfCertServerCfg* srv, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const uint8_t* signer_cert, size_t signer_cert_len, - const WolfCertKey* signer_key, - const uint8_t* serial, size_t serial_len, - WolfCertScepResult* out) +/* GetCACaps reply into *out (RFC 8894 section 3.5.2). */ +static void scep_finish_caps(const WolfCertHttpResponse* resp, + WolfCertScepCaps* out) { - if (out == NULL) - return WOLFCERT_ERR_BAD_ARG; + const char* b = (const char*)resp->body; - memset(out, 0, sizeof(*out)); - out->fail_info = -1; + out->post_pki_operation = has_cap(b, resp->body_len, "POSTPKIOperation"); + out->renewal = has_cap(b, resp->body_len, "Renewal"); + out->sha256 = has_cap(b, resp->body_len, "SHA-256"); + out->sha384 = has_cap(b, resp->body_len, "SHA-384"); + out->sha512 = has_cap(b, resp->body_len, "SHA-512"); + out->aes = has_cap(b, resp->body_len, "AES"); + out->scep_standard = has_cap(b, resp->body_len, "SCEPStandard"); + out->get_next_ca_cert = has_cap(b, resp->body_len, "GetNextCACert"); - if (srv == NULL || ra_cert == NULL || ra_cert_len == 0 || - ca_bundle == NULL || ca_bundle_len == 0 || - signer_cert == NULL || signer_cert_len == 0 || signer_key == NULL || - serial == NULL || serial_len == 0) - return WOLFCERT_ERR_BAD_ARG; + /* RFC 8894 section 3.5.2, Table 7 */ + if (out->scep_standard) { + out->post_pki_operation = 1; + out->aes = 1; + out->sha256 = 1; + } +} - if (signer_key->type != WOLFCERT_KEY_RSA) - return WOLFCERT_ERR(WOLFCERT_ERR_UNSUPPORTED, "scep", - "SCEP (RFC 8894) requires an RSA signer for pkiMessage"); - void* heap = srv->heap ? srv->heap : wolfcert_default_heap(); - int trc = scep_check_cfg(srv, heap); - if (trc != WOLFCERT_OK) - return trc; - - WolfCertBuffer ias = { 0 }; - uint8_t* key_der = NULL; - size_t key_der_len = 0; - const uint8_t* want_issuer = NULL; - size_t want_issuer_len = 0; - const uint8_t* want_serial = NULL; - size_t want_serial_len = 0; - - int rc = wolfcert_scep_issuer_and_serial(ra_cert, ra_cert_len, - serial, serial_len, &ias, heap); - - /* Match the response against the issuer and serial as sent on the wire. */ - if (rc == WOLFCERT_OK) - rc = wolfcert_scep_parse_issuer_and_serial(ias.data, ias.len, - &want_issuer, &want_issuer_len, - &want_serial, &want_serial_len); +/* GetCACert reply into *out_ca: a certs-only PKCS#7, or one raw DER cert. */ +static int scep_finish_ca_cert(const WolfCertHttpResponse* resp, + WolfCertEncoding enc, WolfCertBuffer* out_ca, + void* heap) +{ + static const char ca_ra_type[] = "application/x-x509-ca-ra-cert"; + const char* p; + uint8_t* buf; + size_t cap; + size_t len; + int is_p7 = 0; + int n; - if (rc == WOLFCERT_OK) - rc = rsa_key_to_der(signer_key, heap, &key_der, &key_der_len); + if (resp->body_len == 0) + return WOLFCERT_ERR(WOLFCERT_ERR_HTTP, "scep", + "GetCACert: 200 response carried no certificate"); - if (rc == WOLFCERT_OK) - rc = do_scep_round_trip(srv, caps, ra_cert, ra_cert_len, - ca_bundle, ca_bundle_len, - signer_cert, signer_cert_len, - key_der, key_der_len, - "21", ias.data, ias.len, NULL, 0, out); - - if (rc == WOLFCERT_OK && out->status == WOLFCERT_SCEP_STATUS_SUCCESS) { - int has = wolfcert_scep_pem_has_cert(out->cert_pem.data, - out->cert_pem.len, - want_issuer, want_issuer_len, - want_serial, want_serial_len, heap); - if (has != 1) { - wolfcert_buffer_free(&out->cert_pem); - out->status = WOLFCERT_SCEP_STATUS_UNSET; - rc = (has < 0) ? has : WOLFCERT_ERR(WOLFCERT_ERR_PROTOCOL, "scep", - "GetCert returned no certificate with the requested issuer and serial"); - } + /* Media types compare case-insensitively; ';' parameters are ignored. */ + if (resp->content_type != NULL && + wolfcert_ascii_ncasecmp(resp->content_type, ca_ra_type, + sizeof(ca_ra_type) - 1) == 0) { + p = resp->content_type + sizeof(ca_ra_type) - 1; + while (*p == ' ' || *p == '\t') + ++p; + is_p7 = (*p == '\0' || *p == ';'); } + if (!is_p7) + is_p7 = wolfcert_scep_is_signed_data(resp->body, resp->body_len); + + if (is_p7 && enc == WOLFCERT_ENCODING_DER) + return wolfcert_pkcs7_certs_to_der(resp->body, resp->body_len, out_ca, + heap); + if (is_p7) + return wolfcert_pkcs7_certs_to_pem(resp->body, resp->body_len, out_ca, + heap); + + cap = (enc == WOLFCERT_ENCODING_DER) ? resp->body_len + : resp->body_len * 2 + 256; + buf = (uint8_t*)WOLFCERT_XMALLOC(cap, heap); + if (buf == NULL) + return WOLFCERT_ERR_MEMORY; - wolfcert_buffer_free(&ias); - if (key_der != NULL) { - wc_ForceZero(key_der, (word32)key_der_len); - WOLFCERT_XFREE(key_der, heap); + if (enc == WOLFCERT_ENCODING_DER) { + memcpy(buf, resp->body, resp->body_len); + len = resp->body_len; + } + else { + n = wc_DerToPem(resp->body, (word32)resp->body_len, buf, (word32)cap, + CERT_TYPE); + if (n <= 0) { + WOLFCERT_XFREE(buf, heap); + return WOLFCERT_ERR_CRYPTO; + } + len = (size_t)n; } - return rc; + out_ca->data = buf; + out_ca->len = len; + out_ca->heap = heap; + return WOLFCERT_OK; } -int wolfcert_scep_get_next_ca_cert(const WolfCertServerCfg* srv, - const uint8_t* current_ca_der, - size_t current_ca_len, - WolfCertBuffer* out_next_ca_pem) -{ - if (srv == NULL || srv->server_url == NULL || - current_ca_der == NULL || out_next_ca_pem == NULL) - return WOLFCERT_ERR_BAD_ARG; - - void* heap = srv->heap ? srv->heap : wolfcert_default_heap(); - int trc = scep_check_cfg(srv, heap); - if (trc != WOLFCERT_OK) - return trc; - - - char* url = wolfcert_scep_build_getca_url(srv->server_url, "GetNextCACert", - srv->proto_opts.scep.ca_id, heap); - if (url == NULL) - return WOLFCERT_ERR_MEMORY; - - WolfCertHttpRequest req = { .method = "GET", .url = url }; - fill_common(srv, &req); +enum scep_session_op { + SCEP_OP_GET_CA_CAPS, + SCEP_OP_GET_CA_CERT, + SCEP_OP_GET_NEXT_CA_CERT, + /* The PKIOperations, from here to the end. */ + SCEP_OP_PKCS_REQ, + SCEP_OP_RENEWAL, + SCEP_OP_GET_CERT_INITIAL, + SCEP_OP_GET_CERT +}; - WolfCertHttpResponse resp = { 0 }; - int rc = wolfcert_http_request(&req, &resp); +/* The arguments of one SCEP call. cert is the signer or the cert being + * renewed, ca_bundle the current CA for GetNextCACert; one output is set. */ +typedef struct { + const WolfCertScepCaps* caps; + const uint8_t* ra_cert; + size_t ra_cert_len; + const uint8_t* ca_bundle; + size_t ca_bundle_len; + const uint8_t* cert; + size_t cert_len; + const WolfCertKey* key; + const uint8_t* csr; + size_t csr_len; + const uint8_t* txid; + size_t txid_len; + const uint8_t* serial; + size_t serial_len; + WolfCertScepCaps* caps_out; + WolfCertBuffer* buf_out; + WolfCertScepResult* res_out; + enum scep_session_op op; + WolfCertEncoding enc; +} ScepCall; + +static const void* scep_call_out(const ScepCall* c) +{ + if (c->res_out != NULL) + return c->res_out; + if (c->buf_out != NULL) + return c->buf_out; + return c->caps_out; +} - WOLFCERT_XFREE(url, heap); - if (rc != WOLFCERT_OK) - return rc; +static int scep_call_check(const ScepCall* c) +{ + int pki = c->op >= SCEP_OP_PKCS_REQ; + int ok; - if (resp.status_code == 404) { - wolfcert_http_response_free(&resp); - return WOLFCERT_ERR_NOT_FOUND; + switch (c->op) { + case SCEP_OP_GET_CA_CAPS: + ok = c->caps_out != NULL; + break; + case SCEP_OP_GET_CA_CERT: + ok = c->buf_out != NULL; + break; + case SCEP_OP_GET_NEXT_CA_CERT: + ok = c->buf_out != NULL && c->ca_bundle != NULL && + c->ca_bundle_len > 0; + break; + case SCEP_OP_PKCS_REQ: + ok = c->csr != NULL && c->csr_len > 0; + break; + case SCEP_OP_RENEWAL: + ok = c->cert != NULL && c->cert_len > 0 && + c->csr != NULL && c->csr_len > 0; + break; + case SCEP_OP_GET_CERT_INITIAL: + ok = (c->cert == NULL || c->cert_len > 0) && + c->csr != NULL && c->csr_len > 0 && + c->txid != NULL && c->txid_len > 0 && + wolfcert_is_printable_string(c->txid, c->txid_len); + break; + case SCEP_OP_GET_CERT: + ok = c->cert != NULL && c->cert_len > 0 && + c->serial != NULL && c->serial_len > 0; + break; + default: + ok = 0; + break; } - if (resp.status_code != 200) { - wolfcert_http_response_free(&resp); - return WOLFCERT_ERR_HTTP; - } + if (ok && pki) + ok = c->res_out != NULL && c->ra_cert != NULL && c->ra_cert_len > 0 && + c->ca_bundle != NULL && c->ca_bundle_len > 0 && c->key != NULL; + if (!ok) + return WOLFCERT_ERR_BAD_ARG; - /* RFC 8894 section 4.7.1: the current CA signs a SignedData whose content - * is a certs-only bundle holding the next CA cert. */ - rc = wolfcert_scep_verify_next_ca_response(resp.body, resp.body_len, - current_ca_der, current_ca_len, out_next_ca_pem, heap); + if (pki && c->key->type != WOLFCERT_KEY_RSA) + return WOLFCERT_ERR(WOLFCERT_ERR_UNSUPPORTED, "scep", + "SCEP (RFC 8894) requires an RSA signer for pkiMessage; " + "Ed25519/Ed448/ML-DSA are not permitted"); - wolfcert_http_response_free(&resp); - return rc; + return WOLFCERT_OK; } -/* The PKIOperation in the session's single in-flight slot. */ -enum scep_session_op { - SCEP_SESS_OP_PKCS_REQ = 0, - SCEP_SESS_OP_RENEWAL, - SCEP_SESS_OP_GET_CERT_INITIAL -}; - struct WolfCertScepSession { WolfCertHttpSession* http; char* server_url; /* full SCEP endpoint URL, owned */ void* heap; - int nonblocking; /* opened via _open_async */ WolfCertScepTxidMode txid_mode; WolfCertScepContentCipher content_cipher; WolfCertScepRenewalMsgType renewal_msg_type; WolfCertScepSigningHash signing_hash; + char* ca_id; /* owned copy, or NULL */ /* In-flight state of the single active round trip. */ int in_active; - int in_op; /* enum scep_session_op, valid when in_active */ + enum scep_session_op in_op; /* valid when in_active */ char* in_url; /* owned request URL */ WolfCertBuffer in_pki; /* built pkiMessage, owned (POST body) */ WolfCertHttpRequest in_req; WolfCertHttpResponse in_resp; - uint8_t* in_ca_bundle; size_t in_ca_bundle_len; /* owned copy */ - uint8_t* in_signer; size_t in_signer_len; /* owned copy */ - uint8_t* in_signer_key; size_t in_signer_key_len; /* owned copy, zeroized */ + const uint8_t* in_ca_bundle; size_t in_ca_bundle_len; /* in use */ + uint8_t* in_ca_bundle_copy; /* owned, for an _nb request */ + const uint8_t* in_signer; size_t in_signer_len; /* in use */ + uint8_t* in_signer_copy; /* owned: _nb copy or self-signed */ + uint8_t* in_signer_key; size_t in_signer_key_len; /* key DER, zeroized */ uint8_t* in_txid; size_t in_txid_len; /* owned, from scep_prepare */ uint8_t in_nonce[SCEP_NONCE_SZ]; - WolfCertScepResult* in_out; + WolfCertBuffer in_ias; /* IssuerAndSubject or IssuerAndSerial sent, owned */ + const void* in_out; /* caller's output, checked on resume */ }; static uint8_t* dup_buf(const uint8_t* src, size_t len, void* heap) @@ -1383,8 +728,9 @@ static uint8_t* dup_buf(const uint8_t* src, size_t len, void* heap) return p; } -static int scep_session_open_common(const WolfCertServerCfg* srv, int nonblocking, - WolfCertScepSession** out) +/* Check srv and set up a session that is not connected yet. */ +static int scep_session_new(const WolfCertServerCfg* srv, + WolfCertScepSession** out) { if (srv == NULL || srv->server_url == NULL || out == NULL) return WOLFCERT_ERR_BAD_ARG; @@ -1401,41 +747,41 @@ static int scep_session_open_common(const WolfCertServerCfg* srv, int nonblockin if (rc != WOLFCERT_OK) return rc; - if (u.tls && !srv->verify_server) { - wolfcert_http_url_free(&u); + int tls = u.tls; + wolfcert_http_url_free(&u); + if (tls && !srv->verify_server) return WOLFCERT_ERR(WOLFCERT_ERR_TLS, "scep", "TLS SCEP endpoint requires server authentication: set verify_server " "or use a plaintext http:// URL"); - } - - char* origin = NULL; - rc = wolfcert_http_url_origin(&u, heap, &origin); - wolfcert_http_url_free(&u); - if (rc != WOLFCERT_OK) - return rc; WolfCertScepSession* s = (WolfCertScepSession*)WOLFCERT_XMALLOC(sizeof(*s), heap); - if (s == NULL) { - WOLFCERT_XFREE(origin, heap); + if (s == NULL) return WOLFCERT_ERR_MEMORY; - } memset(s, 0, sizeof(*s)); - s->heap = heap; - s->nonblocking = nonblocking; + s->heap = heap; s->txid_mode = srv->proto_opts.scep.txid_mode; s->content_cipher = srv->proto_opts.scep.content_cipher; s->renewal_msg_type = srv->proto_opts.scep.renewal_msg_type; s->signing_hash = srv->proto_opts.scep.signing_hash; - s->server_url = wolfcert_strdup(srv->server_url, heap); - if (s->server_url == NULL) { - WOLFCERT_XFREE(s, heap); - WOLFCERT_XFREE(origin, heap); + s->server_url = wolfcert_strdup(srv->server_url, heap); + if (srv->proto_opts.scep.ca_id != NULL) + s->ca_id = wolfcert_strdup(srv->proto_opts.scep.ca_id, heap); + if (s->server_url == NULL || + (srv->proto_opts.scep.ca_id != NULL && s->ca_id == NULL)) { + wolfcert_scep_session_close(s); return WOLFCERT_ERR_MEMORY; } + *out = s; + return WOLFCERT_OK; +} + +static int scep_session_connect(WolfCertScepSession* s, + const WolfCertServerCfg* srv) +{ WolfCertHttpSessionCfg hcfg = { - .base_url = origin, + .base_url = s->server_url, .trust_anchors = srv->trust_anchors, .trust_anchors_len = srv->trust_anchors_len, .verify_server = srv->verify_server, @@ -1446,29 +792,28 @@ static int scep_session_open_common(const WolfCertServerCfg* srv, int nonblockin .client_key = srv->client_key, .client_key_len = srv->client_key_len, .transport = srv->transport, - .heap = heap, + .heap = s->heap, }; - rc = wolfcert_http_session_open(&hcfg, &s->http); - - WOLFCERT_XFREE(origin, heap); - if (rc != WOLFCERT_OK) { - WOLFCERT_XFREE(s->server_url, heap); - WOLFCERT_XFREE(s, heap); - return rc; - } - *out = s; - return WOLFCERT_OK; + return wolfcert_http_session_open(&hcfg, &s->http); } int wolfcert_scep_session_open(const WolfCertServerCfg* srv, WolfCertScepSession** out) { - return scep_session_open_common(srv, 0, out); -} + WolfCertScepSession* s = NULL; + int rc; -int wolfcert_scep_session_open_async(const WolfCertServerCfg* srv, WolfCertScepSession** out) -{ - return scep_session_open_common(srv, 1, out); + if (out == NULL) + return WOLFCERT_ERR_BAD_ARG; + + rc = scep_session_new(srv, &s); + if (rc == WOLFCERT_OK) + rc = scep_session_connect(s, srv); + if (rc == WOLFCERT_OK) + *out = s; + else + wolfcert_scep_session_close(s); + return rc; } int wolfcert_scep_session_fd(const WolfCertScepSession* s) @@ -1476,19 +821,27 @@ int wolfcert_scep_session_fd(const WolfCertScepSession* s) return s != NULL ? wolfcert_http_session_fd(s->http) : -1; } -static void scep_async_reset(WolfCertScepSession* s) +/* Free the request in flight; its response stays for the parse. */ +static void scep_session_free_request(WolfCertScepSession* s) { WOLFCERT_XFREE(s->in_url, s->heap); s->in_url = NULL; wolfcert_buffer_free(&s->in_pki); + memset(&s->in_req, 0, sizeof(s->in_req)); +} + +static void scep_session_reset(WolfCertScepSession* s) +{ + scep_session_free_request(s); wolfcert_http_response_free(&s->in_resp); - memset(&s->in_req, 0, sizeof(s->in_req)); memset(&s->in_resp, 0, sizeof(s->in_resp)); - WOLFCERT_XFREE(s->in_ca_bundle, s->heap); + WOLFCERT_XFREE(s->in_ca_bundle_copy, s->heap); + s->in_ca_bundle_copy = NULL; s->in_ca_bundle = NULL; s->in_ca_bundle_len = 0; - WOLFCERT_XFREE(s->in_signer, s->heap); + WOLFCERT_XFREE(s->in_signer_copy, s->heap); + s->in_signer_copy = NULL; s->in_signer = NULL; s->in_signer_len = 0; if (s->in_signer_key != NULL) { @@ -1501,6 +854,7 @@ static void scep_async_reset(WolfCertScepSession* s) s->in_txid = NULL; s->in_txid_len = 0; wc_ForceZero(s->in_nonce, (word32)sizeof(s->in_nonce)); + wolfcert_buffer_free(&s->in_ias); s->in_out = NULL; s->in_active = 0; } @@ -1510,342 +864,608 @@ void wolfcert_scep_session_close(WolfCertScepSession* s) if (s == NULL) return; - scep_async_reset(s); + scep_session_reset(s); if (s->http) wolfcert_http_session_close(s->http); WOLFCERT_XFREE(s->server_url, s->heap); + WOLFCERT_XFREE(s->ca_id, s->heap); WOLFCERT_XFREE(s, s->heap); } -/* Copies the finish-phase inputs so they outlive the pumps. */ -static int scep_session_begin(WolfCertScepSession* s, const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const uint8_t* signer_cert, size_t signer_cert_len, - const uint8_t* signer_key, size_t signer_key_len, - const char* msg_type, int op, - const uint8_t* envelope_content, size_t envelope_content_len, - const uint8_t* txid_override, size_t txid_override_len, - WolfCertScepResult* out) +/* Clear the caller's output, unless it is the in-flight request's. */ +static void scep_call_clear(const WolfCertScepSession* s, const ScepCall* c) +{ + if (s != NULL && s->in_active && scep_call_out(c) == s->in_out) + return; + + if (c->res_out != NULL) { + memset(c->res_out, 0, sizeof(*c->res_out)); + c->res_out->fail_info = -1; + } + if (c->caps_out != NULL) + memset(c->caps_out, 0, sizeof(*c->caps_out)); + if (c->buf_out != NULL) + memset(c->buf_out, 0, sizeof(*c->buf_out)); +} + +/* Put c's request in flight; url passes to s. post sends s->in_pki. */ +static void scep_session_start(WolfCertScepSession* s, const ScepCall* c, + char* url, int post) +{ + s->in_url = url; + s->in_req = (WolfCertHttpRequest){ + .method = post ? "POST" : "GET", + .url = url, + .heap = s->heap, + }; + if (post) { + s->in_req.content_type = "application/x-pki-message"; + s->in_req.body = s->in_pki.data; + s->in_req.body_len = s->in_pki.len; + } + s->in_op = c->op; + s->in_out = scep_call_out(c); + s->in_active = 1; +} + +/* A resumed _nb poll must name the in-flight operation and output. */ +static int scep_session_resume_check(const WolfCertScepSession* s, + const ScepCall* c) +{ + if (s->in_op != c->op) + return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", + "a different SCEP operation is already in flight on this session"); + if (scep_call_out(c) != s->in_out) + return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", + "output pointer differs from the in-flight request; pass the " + "same pointer to each poll call"); + return WOLFCERT_OK; +} + +/* Build c's pkiMessage, signed with s->in_signer and s->in_signer_key; the + * transactionID and senderNonce stay in s to validate the CertRep. */ +static int scep_prepare(WolfCertScepSession* s, const ScepCall* c, + const char* msg_type, + const uint8_t* envelope_content, size_t envelope_content_len, + WolfCertBuffer* out_pki) +{ + void* heap = s->heap; + const WolfCertScepCaps* caps = c->caps; + ScepTxidSel txid_sel = { + .id = c->txid, .id_len = c->txid_len, .mode = s->txid_mode + }; + int hash_oid = wolfcert_scep_pick_hash_oid(caps, s->signing_hash); + int enc_oid; + + if (hash_oid < 0) + return hash_oid; + + /* AUTO follows RFC 8894: GetCACaps "AES" means AES-128-CBC, else 3DES. */ + switch (s->content_cipher) { + case WOLFCERT_SCEP_CIPHER_AES128: + enc_oid = AES128CBCb; + break; + case WOLFCERT_SCEP_CIPHER_AES256: +#if !defined(WOLFSSL_AES_256) || !defined(HAVE_AES_CBC) + return WOLFCERT_ERR(WOLFCERT_ERR_UNSUPPORTED, "scep", + "AES-256-CBC content cipher requested but wolfSSL lacks it"); +#else + enc_oid = AES256CBCb; + break; +#endif + case WOLFCERT_SCEP_CIPHER_DES3: +#ifdef NO_DES3 + return WOLFCERT_ERR(WOLFCERT_ERR_UNSUPPORTED, "scep", + "3DES content cipher requested but wolfSSL was built NO_DES3"); +#else + enc_oid = DES3b; + break; +#endif + case WOLFCERT_SCEP_CIPHER_AUTO: + default: + if (caps != NULL && caps->aes) { + enc_oid = AES128CBCb; + } + else { +#ifdef NO_DES3 + return WOLFCERT_ERR(WOLFCERT_ERR_UNSUPPORTED, "scep", + "no usable content cipher: CA does not advertise AES and " + "wolfSSL lacks the 3DES fallback"); +#else + enc_oid = DES3b; +#endif + } + break; + } + + WolfCertBuffer env = { 0 }; + int rc = wolfcert_scep_envelop(c->ra_cert, c->ra_cert_len, + envelope_content, envelope_content_len, + enc_oid, &env, heap); + if (rc != WOLFCERT_OK) + return rc; + + WC_RNG rng; + int rng_rc = wc_InitRng_ex(&rng, heap, WOLFCERT_DEVID_SOFTWARE); + if (rng_rc != 0) { + wolfcert_buffer_free(&env); + return WOLFCERT_ERR_WC(rng_rc, "scep", "RNG init failed"); + } + + rng_rc = wc_RNG_GenerateBlock(&rng, s->in_nonce, SCEP_NONCE_SZ); + if (rng_rc != 0) { + wc_FreeRng(&rng); + wolfcert_buffer_free(&env); + return WOLFCERT_ERR_WC(rng_rc, "scep", "RNG failed generating senderNonce"); + } + + rc = scep_build_txid(&txid_sel, s->in_signer, s->in_signer_len, &rng, heap, + &s->in_txid, &s->in_txid_len); + wc_FreeRng(&rng); + if (rc != WOLFCERT_OK) { + wolfcert_buffer_free(&env); + return rc; + } + + WolfCertScepAttrs attrs = { + .transaction_id = s->in_txid, .transaction_id_len = s->in_txid_len, + .sender_nonce = s->in_nonce, .sender_nonce_len = SCEP_NONCE_SZ, + .message_type = msg_type, + }; + rc = wolfcert_scep_build_pki_message(env.data, env.len, + s->in_signer, s->in_signer_len, + s->in_signer_key, s->in_signer_key_len, + hash_oid, &attrs, out_pki, heap); + + wolfcert_buffer_free(&env); + return rc; +} + +/* Validate the CertRep in s->in_resp against the request in s and fill + * `out`. */ +static int scep_finish(const WolfCertScepSession* s, WolfCertScepResult* out) +{ + void* heap = s->heap; + WolfCertBuffer resp_env = { 0 }; + WolfCertBuffer inner = { 0 }; + char* status = NULL; + char* fail_info = NULL; + char* resp_mt = NULL; + uint8_t* rx_tid = NULL; + size_t rx_tid_len = 0; + uint8_t* rx_sn = NULL; + size_t rx_sn_len = 0; + uint8_t* rx_rn = NULL; + size_t rx_rn_len = 0; + uint8_t* rx_signer = NULL; + size_t rx_signer_len = 0; + int rc = wolfcert_scep_parse_pki_message(s->in_resp.body, + s->in_resp.body_len, &resp_env, + &rx_tid, &rx_tid_len, &rx_sn, &rx_sn_len, &rx_rn, &rx_rn_len, + &resp_mt, &status, &rx_signer, &rx_signer_len, &fail_info, heap); + + WOLFCERT_XFREE(rx_sn, heap); + + /* RFC 8894: a CertRep is messageType 3 and echoes our transactionID. */ + if (rc == WOLFCERT_OK) { + rc = wolfcert_scep_check_cert_rep(resp_mt, rx_tid, rx_tid_len, + s->in_txid, s->in_txid_len); + if (rc != WOLFCERT_OK) + rc = WOLFCERT_ERR(WOLFCERT_ERR_PROTOCOL, "scep", + "CertRep messageType or transactionID does not " + "match the request"); + } + WOLFCERT_XFREE(resp_mt, heap); + + /* Authenticate the CertRep: its signer's key must match a cert in the + * GetCACert bundle. */ + if (rc == WOLFCERT_OK) { + rc = wolfcert_scep_verify_rep_signer(rx_signer, rx_signer_len, + s->in_ca_bundle, + s->in_ca_bundle_len, heap); + if (rc == WOLFCERT_ERR_MEMORY) + rc = WOLFCERT_ERR(rc, "scep", "out of memory checking the CertRep signer"); + else if (rc != WOLFCERT_OK) + rc = WOLFCERT_ERR(WOLFCERT_ERR_AUTH, "scep", + "CertRep is not signed by the CA/RA certificate"); + } + WOLFCERT_XFREE(rx_signer, heap); + + /* RFC 8894 section 3.2.1.5: recipientNonce must echo our senderNonce. */ + if (rc == WOLFCERT_OK && + (rx_rn == NULL || rx_rn_len != SCEP_NONCE_SZ || + memcmp(rx_rn, s->in_nonce, SCEP_NONCE_SZ) != 0)) { + rc = WOLFCERT_ERR(WOLFCERT_ERR_PROTOCOL, "scep", + "CertRep recipientNonce missing or does not echo " + "senderNonce"); + } + WOLFCERT_XFREE(rx_rn, heap); + + /* RFC 8894 section 3.2.1.3: a CertRep carries pkiStatus 0, 2 or 3. */ + if (rc == WOLFCERT_OK && + (status == NULL || (strcmp(status, "0") != 0 && + strcmp(status, "2") != 0 && + strcmp(status, "3") != 0))) { + rc = WOLFCERT_ERR(WOLFCERT_ERR_PROTOCOL, "scep", + "CertRep pkiStatus missing or not 0, 2 or 3"); + } + + /* RFC 8894 section 3.2.1.4: a FAILURE CertRep carries failInfo 0..4. */ + if (rc == WOLFCERT_OK && strcmp(status, "2") == 0 && + (fail_info == NULL || fail_info[0] < '0' || fail_info[0] > '4' || + fail_info[1] != '\0')) { + rc = WOLFCERT_ERR(WOLFCERT_ERR_PROTOCOL, "scep", + "FAILURE CertRep failInfo missing or not 0..4"); + } + + if (rc == WOLFCERT_OK) { + /* Ownership of rx_tid moves to out. */ + out->transaction_id = rx_tid; + out->transaction_id_len = rx_tid_len; + rx_tid = NULL; + + if (strcmp(status, "3") == 0) { + out->status = WOLFCERT_SCEP_STATUS_PENDING; + } + else if (strcmp(status, "2") == 0) { + out->status = WOLFCERT_SCEP_STATUS_FAILURE; + out->fail_info = fail_info[0] - '0'; + } + else { + rc = wolfcert_scep_deenvelop(s->in_signer, s->in_signer_len, + s->in_signer_key, s->in_signer_key_len, + resp_env.data, resp_env.len, &inner, + heap); + if (rc == WOLFCERT_OK) + rc = wolfcert_pkcs7_certs_to_pem(inner.data, inner.len, + &out->cert_pem, heap); + if (rc == WOLFCERT_OK) + out->status = WOLFCERT_SCEP_STATUS_SUCCESS; + } + } + + WOLFCERT_XFREE(rx_tid, heap); + WOLFCERT_XFREE(status, heap); + WOLFCERT_XFREE(fail_info, heap); + wolfcert_buffer_free(&inner); + wolfcert_buffer_free(&resp_env); + return rc; +} + +/* Sign c's PKIOperation with the caller's cert, or with a transient + * self-signed one when there is none, and put it in flight. */ +static int scep_session_begin_pki_op(WolfCertScepSession* s, const ScepCall* c, + int nb) +{ + void* heap = s->heap; + const char* msg_type = "19"; + const uint8_t* content = c->csr; + size_t content_len = c->csr_len; + WolfCertBuffer pki = { 0 }; + char* url = NULL; + int use_post = 0; + int rc = WOLFCERT_OK; + + c->res_out->heap = heap; + + if (c->cert == NULL) { + rc = wolfcert_scep_self_signed_rsa((RsaKey*)c->key->impl, + c->csr, c->csr_len, + &s->in_signer_copy, + &s->in_signer_len, heap); + s->in_signer = s->in_signer_copy; + } + else { + if (nb) { + s->in_signer_copy = dup_buf(c->cert, c->cert_len, heap); + if (s->in_signer_copy == NULL) + rc = WOLFCERT_ERR_MEMORY; + } + s->in_signer = nb ? s->in_signer_copy : c->cert; + s->in_signer_len = c->cert_len; + } + + if (rc == WOLFCERT_OK) { + if (nb) { + s->in_ca_bundle_copy = dup_buf(c->ca_bundle, c->ca_bundle_len, heap); + if (s->in_ca_bundle_copy == NULL) + rc = WOLFCERT_ERR_MEMORY; + } + s->in_ca_bundle = nb ? s->in_ca_bundle_copy : c->ca_bundle; + s->in_ca_bundle_len = c->ca_bundle_len; + } + + switch (c->op) { + case SCEP_OP_RENEWAL: + msg_type = scep_renewal_msg_type(s->renewal_msg_type); + break; + case SCEP_OP_GET_CERT_INITIAL: + msg_type = "20"; + if (rc == WOLFCERT_OK) + rc = wolfcert_scep_issuer_and_subject(c->ra_cert, c->ra_cert_len, + c->csr, c->csr_len, + &s->in_ias, heap); + break; + case SCEP_OP_GET_CERT: + msg_type = "21"; + if (rc == WOLFCERT_OK) + rc = wolfcert_scep_issuer_and_serial(c->ra_cert, c->ra_cert_len, + c->serial, c->serial_len, + &s->in_ias, heap); + break; + default: + break; + } + if (s->in_ias.data != NULL) { + content = s->in_ias.data; + content_len = s->in_ias.len; + } + + if (rc == WOLFCERT_OK) + rc = rsa_key_to_der(c->key, heap, &s->in_signer_key, + &s->in_signer_key_len); + if (rc == WOLFCERT_OK) + rc = scep_prepare(s, c, msg_type, content, content_len, &pki); + if (rc == WOLFCERT_OK) + rc = scep_build_transport(s->server_url, c->caps, pki.data, pki.len, + heap, &url, &use_post); + + if (rc == WOLFCERT_OK) { + s->in_pki = pki; + scep_session_start(s, c, url, use_post); + } + else { + wolfcert_buffer_free(&pki); + scep_session_reset(s); + } + return rc; +} + +/* Put the GetCACaps, GetCACert or GetNextCACert request for c in flight. */ +static int scep_session_begin_get(WolfCertScepSession* s, const ScepCall* c, + int nb) { - void* heap = s->heap; - out->heap = heap; - out->fail_info = -1; - - /* dup_buf also returns NULL for a zero length, so reject empty inputs. */ - if (ca_bundle_len == 0 || signer_cert_len == 0 || signer_key_len == 0) - return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", - "ca_bundle, signer cert and signer key must be non-empty"); - - s->in_ca_bundle = dup_buf(ca_bundle, ca_bundle_len, heap); - s->in_ca_bundle_len = ca_bundle_len; - s->in_signer = dup_buf(signer_cert, signer_cert_len, heap); - s->in_signer_len = signer_cert_len; - s->in_signer_key = dup_buf(signer_key, signer_key_len, heap); - s->in_signer_key_len = signer_key_len; - if (s->in_ca_bundle == NULL || s->in_signer == NULL || s->in_signer_key == NULL) { - scep_async_reset(s); + const char* name = (c->op == SCEP_OP_GET_CA_CAPS) ? "GetCACaps" : + (c->op == SCEP_OP_GET_CA_CERT) ? "GetCACert" : + "GetNextCACert"; + char* url = wolfcert_scep_build_getca_url(s->server_url, name, s->ca_id, + s->heap); + if (url == NULL) return WOLFCERT_ERR_MEMORY; - } - - ScepTxidSel txid_sel = { - .id = txid_override, .id_len = txid_override_len, .mode = s->txid_mode - }; - WolfCertBuffer pki = { 0 }; - int rc = scep_prepare(heap, caps, ra_cert, ra_cert_len, - signer_cert, signer_cert_len, signer_key, signer_key_len, - msg_type, envelope_content, envelope_content_len, - &txid_sel, s->content_cipher, s->signing_hash, - &pki, &s->in_txid, &s->in_txid_len, s->in_nonce); - if (rc != WOLFCERT_OK) { - scep_async_reset(s); - return rc; + if (c->op == SCEP_OP_GET_NEXT_CA_CERT) { + if (nb) { + s->in_ca_bundle_copy = dup_buf(c->ca_bundle, c->ca_bundle_len, + s->heap); + if (s->in_ca_bundle_copy == NULL) { + WOLFCERT_XFREE(url, s->heap); + return WOLFCERT_ERR_MEMORY; + } + } + s->in_ca_bundle = nb ? s->in_ca_bundle_copy : c->ca_bundle; + s->in_ca_bundle_len = c->ca_bundle_len; } - char* url = NULL; - int use_post = 0; - rc = scep_build_transport(s->server_url, caps, pki.data, pki.len, heap, - &url, &use_post); - if (rc != WOLFCERT_OK) { - wolfcert_buffer_free(&pki); - scep_async_reset(s); - return rc; - } + scep_session_start(s, c, url, 0); + return WOLFCERT_OK; +} - s->in_pki = pki; /* ownership moves to the session */ - s->in_url = url; - /* The session ignores a per-request max_response_bytes; it uses the cap - * from WolfCertHttpSessionCfg. */ - s->in_req = (WolfCertHttpRequest){ - .method = use_post ? "POST" : "GET", - .url = url, - .heap = heap, - }; - if (use_post) { - s->in_req.content_type = "application/x-pki-message"; - s->in_req.body = s->in_pki.data; - s->in_req.body_len = s->in_pki.len; - } - s->in_out = out; - s->in_op = op; - s->in_active = 1; +/* A GetCert reply must hold the requested issuer and serial. */ +static int scep_check_get_cert(const WolfCertScepSession* s, + WolfCertScepResult* out) +{ + const uint8_t* issuer = NULL; + size_t issuer_len = 0; + const uint8_t* serial = NULL; + size_t serial_len = 0; + int has; + int rc = wolfcert_scep_parse_issuer_and_serial(s->in_ias.data, s->in_ias.len, + &issuer, &issuer_len, + &serial, &serial_len); + if (rc == WOLFCERT_OK) + has = wolfcert_scep_pem_has_cert(out->cert_pem.data, out->cert_pem.len, + issuer, issuer_len, serial, serial_len, + s->heap); + else + has = rc; + if (has == 1) + return WOLFCERT_OK; - return WOLFCERT_OK; + wolfcert_buffer_free(&out->cert_pem); + out->status = WOLFCERT_SCEP_STATUS_UNSET; + return (has < 0) ? has : WOLFCERT_ERR(WOLFCERT_ERR_PROTOCOL, "scep", + "GetCert returned no certificate with the requested issuer and serial"); } -/* `rc` is the transport result, already past any WANT_* handling. */ -static int scep_session_finish(WolfCertScepSession* s, int rc) +/* Parse s->in_resp into c's output and end the request. */ +static int scep_session_finish(WolfCertScepSession* s, const ScepCall* c) { - if (rc != WOLFCERT_OK) { - scep_async_reset(s); - return rc; - } + const WolfCertHttpResponse* r = &s->in_resp; + int rc; - if (s->in_resp.status_code != 200) { - scep_async_reset(s); - return WOLFCERT_ERR_HTTP; + if (s->in_op == SCEP_OP_GET_NEXT_CA_CERT && r->status_code == 404) { + rc = WOLFCERT_ERR_NOT_FOUND; + } + else if (r->status_code != 200) { + rc = WOLFCERT_ERR_HTTP; + } + else if (s->in_op == SCEP_OP_GET_CA_CAPS) { + scep_finish_caps(r, c->caps_out); + rc = WOLFCERT_OK; + } + else if (s->in_op == SCEP_OP_GET_CA_CERT) { + rc = scep_finish_ca_cert(r, c->enc, c->buf_out, s->heap); + } + else if (s->in_op == SCEP_OP_GET_NEXT_CA_CERT) { + /* RFC 8894 section 4.7.1: the current CA signs a SignedData whose + * content is a certs-only bundle holding the next CA cert. */ + rc = wolfcert_scep_verify_next_ca_response(r->body, r->body_len, + s->in_ca_bundle, s->in_ca_bundle_len, c->buf_out, s->heap); + } + else { + rc = scep_finish(s, c->res_out); + if (rc == WOLFCERT_OK && s->in_op == SCEP_OP_GET_CERT && + c->res_out->status == WOLFCERT_SCEP_STATUS_SUCCESS) + rc = scep_check_get_cert(s, c->res_out); } - rc = scep_finish(s->heap, s->in_resp.body, s->in_resp.body_len, - s->in_ca_bundle, s->in_ca_bundle_len, - s->in_signer, s->in_signer_len, - s->in_signer_key, s->in_signer_key_len, - s->in_txid, s->in_txid_len, s->in_nonce, s->in_out); - scep_async_reset(s); + scep_session_reset(s); return rc; } -static int scep_session_drive_sync(WolfCertScepSession* s) +/* Build c's request into s and put it in flight. */ +static int scep_session_begin(WolfCertScepSession* s, const ScepCall* c, int nb) { - int rc = wolfcert_http_session_request(s->http, &s->in_req, &s->in_resp); - return scep_session_finish(s, rc); + if (c->op >= SCEP_OP_PKCS_REQ) + return scep_session_begin_pki_op(s, c, nb); + return scep_session_begin_get(s, c, nb); } -static int scep_session_drive_nb(WolfCertScepSession* s) +/* Send or continue the request in flight, then parse the reply into c's + * output. */ +static int scep_session_send(WolfCertScepSession* s, const ScepCall* c, int nb) { - int rc = wolfcert_http_session_request_nb(s->http, &s->in_req, &s->in_resp); - if (rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE) + int rc; + + if (nb) + rc = wolfcert_http_session_request_nb(s->http, &s->in_req, &s->in_resp); + else + rc = wolfcert_http_session_request(s->http, &s->in_req, &s->in_resp); + + if (nb && (rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE)) return rc; - return scep_session_finish(s, rc); -} -/* A resumed _nb poll must name the in-flight operation and result. */ -static int scep_session_resume_check(const WolfCertScepSession* s, int expected_op, - const WolfCertScepResult* out) -{ - if (s->in_op != expected_op) - return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", - "a different SCEP operation is already in flight on this session"); - if (out != s->in_out) - return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", - "result pointer differs from the in-flight request; pass the " - "same WolfCertScepResult* to each poll call"); - return WOLFCERT_OK; + if (rc == WOLFCERT_OK) { + scep_session_free_request(s); + return scep_session_finish(s, c); + } + + scep_session_reset(s); + return rc; } -static int scep_session_begin_pkcs_req(WolfCertScepSession* s, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const WolfCertKey* new_key, const uint8_t* csr_der, size_t csr_der_len, - WolfCertScepResult* out) +/* Run c on s, or continue the _nb request in flight, and parse the reply. */ +static int scep_session_call(WolfCertScepSession* s, const ScepCall* c, int nb) { - if (new_key->type != WOLFCERT_KEY_RSA) - return WOLFCERT_ERR(WOLFCERT_ERR_UNSUPPORTED, "scep", - "SCEP (RFC 8894) requires an RSA signer for pkiMessage"); + int rc; - void* heap = s->heap; - uint8_t* signer_der = NULL; - size_t signer_len = 0; - int rc = wolfcert_scep_self_signed_rsa((RsaKey*)new_key->impl, - csr_der, csr_der_len, - &signer_der, &signer_len, heap); + if (scep_call_out(c) == NULL) + return WOLFCERT_ERR_BAD_ARG; + + scep_call_clear(s, c); + if (s == NULL) + return WOLFCERT_ERR_BAD_ARG; + + if (s->in_active && !nb) + return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", + "a SCEP request is in flight on this session; continue it with " + "the _nb call"); + + if (s->in_active) { + rc = scep_session_resume_check(s, c); + } + else { + rc = scep_call_check(c); + if (rc == WOLFCERT_OK) + rc = scep_session_begin(s, c, nb); + } if (rc != WOLFCERT_OK) return rc; - uint8_t* key_der = NULL; - size_t key_der_len = 0; - rc = rsa_key_to_der(new_key, heap, &key_der, &key_der_len); - if (rc != WOLFCERT_OK) { - WOLFCERT_XFREE(signer_der, heap); - return rc; - } + return scep_session_send(s, c, nb); +} - rc = scep_session_begin(s, caps, ra_cert, ra_cert_len, ca_bundle, ca_bundle_len, - signer_der, signer_len, key_der, key_der_len, - "19", SCEP_SESS_OP_PKCS_REQ, - csr_der, csr_der_len, NULL, 0, out); +int wolfcert_scep_session_get_ca_caps(WolfCertScepSession* s, + WolfCertScepCaps* out_caps) +{ + ScepCall c = { .op = SCEP_OP_GET_CA_CAPS, .caps_out = out_caps }; - WOLFCERT_XFREE(signer_der, heap); - wc_ForceZero(key_der, (word32)key_der_len); - WOLFCERT_XFREE(key_der, heap); - return rc; + return scep_session_call(s, &c, 0); } -static int scep_session_begin_renewal(WolfCertScepSession* s, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const uint8_t* current_cert, size_t current_cert_len, - const WolfCertKey* current_key, const uint8_t* csr_der, size_t csr_der_len, - WolfCertScepResult* out) +int wolfcert_scep_session_get_ca_caps_nb(WolfCertScepSession* s, + WolfCertScepCaps* out_caps) { - if (current_key->type != WOLFCERT_KEY_RSA) - return WOLFCERT_ERR(WOLFCERT_ERR_UNSUPPORTED, "scep", - "SCEP (RFC 8894) requires an RSA signer for pkiMessage"); + ScepCall c = { .op = SCEP_OP_GET_CA_CAPS, .caps_out = out_caps }; - void* heap = s->heap; - uint8_t* key_der = NULL; - size_t key_der_len = 0; - int rc = rsa_key_to_der(current_key, heap, &key_der, &key_der_len); - if (rc != WOLFCERT_OK) - return rc; + return scep_session_call(s, &c, 1); +} - rc = scep_session_begin(s, caps, ra_cert, ra_cert_len, ca_bundle, ca_bundle_len, - current_cert, current_cert_len, key_der, key_der_len, - scep_renewal_msg_type(s->renewal_msg_type), - SCEP_SESS_OP_RENEWAL, - csr_der, csr_der_len, NULL, 0, out); +int wolfcert_scep_session_get_ca_cert_enc(WolfCertScepSession* s, + WolfCertEncoding enc, + WolfCertBuffer* out_ca) +{ + ScepCall c = { .op = SCEP_OP_GET_CA_CERT, .enc = enc, .buf_out = out_ca }; - wc_ForceZero(key_der, (word32)key_der_len); - WOLFCERT_XFREE(key_der, heap); - return rc; + return scep_session_call(s, &c, 0); } -static int scep_session_begin_get_cert_initial(WolfCertScepSession* s, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const uint8_t* signer_cert, size_t signer_cert_len, - const WolfCertKey* signer_key, - const uint8_t* csr_der, size_t csr_der_len, - const uint8_t* transaction_id, size_t transaction_id_len, - WolfCertScepResult* out) +int wolfcert_scep_session_get_ca_cert_enc_nb(WolfCertScepSession* s, + WolfCertEncoding enc, + WolfCertBuffer* out_ca) { - if (signer_key->type != WOLFCERT_KEY_RSA) - return WOLFCERT_ERR(WOLFCERT_ERR_UNSUPPORTED, "scep", - "SCEP (RFC 8894) requires an RSA signer for pkiMessage"); + ScepCall c = { .op = SCEP_OP_GET_CA_CERT, .enc = enc, .buf_out = out_ca }; - void* heap = s->heap; - WolfCertBuffer ias = { 0 }; - int rc = wolfcert_scep_issuer_and_subject(ra_cert, ra_cert_len, - csr_der, csr_der_len, &ias, heap); - if (rc != WOLFCERT_OK) - return rc; + return scep_session_call(s, &c, 1); +} - uint8_t* key_der = NULL; - size_t key_der_len = 0; - rc = rsa_key_to_der(signer_key, heap, &key_der, &key_der_len); - if (rc != WOLFCERT_OK) { - wolfcert_buffer_free(&ias); - return rc; - } +int wolfcert_scep_session_get_next_ca_cert(WolfCertScepSession* s, + const uint8_t* current_ca_der, + size_t current_ca_len, + WolfCertBuffer* out_next_ca_pem) +{ + ScepCall c = { .op = SCEP_OP_GET_NEXT_CA_CERT, + .ca_bundle = current_ca_der, .ca_bundle_len = current_ca_len, + .buf_out = out_next_ca_pem }; - /* A pending PKCSReq has no signer cert; regenerate the transient one. */ - uint8_t* derived = NULL; - size_t derived_len = 0; - const uint8_t* eff = signer_cert; - size_t eff_len = signer_cert_len; - if (signer_cert == NULL) { - rc = wolfcert_scep_self_signed_rsa((RsaKey*)signer_key->impl, - csr_der, csr_der_len, - &derived, &derived_len, heap); - if (rc != WOLFCERT_OK) { - wolfcert_buffer_free(&ias); - wc_ForceZero(key_der, (word32)key_der_len); - WOLFCERT_XFREE(key_der, heap); - return rc; - } - eff = derived; - eff_len = derived_len; - } + return scep_session_call(s, &c, 0); +} - rc = scep_session_begin(s, caps, ra_cert, ra_cert_len, ca_bundle, ca_bundle_len, - eff, eff_len, key_der, key_der_len, - "20", SCEP_SESS_OP_GET_CERT_INITIAL, ias.data, ias.len, - transaction_id, transaction_id_len, out); +int wolfcert_scep_session_get_next_ca_cert_nb(WolfCertScepSession* s, + const uint8_t* current_ca_der, + size_t current_ca_len, + WolfCertBuffer* out_next_ca_pem) +{ + ScepCall c = { .op = SCEP_OP_GET_NEXT_CA_CERT, + .ca_bundle = current_ca_der, .ca_bundle_len = current_ca_len, + .buf_out = out_next_ca_pem }; - wolfcert_buffer_free(&ias); - wc_ForceZero(key_der, (word32)key_der_len); - WOLFCERT_XFREE(key_der, heap); - WOLFCERT_XFREE(derived, heap); - return rc; + return scep_session_call(s, &c, 1); } int wolfcert_scep_session_pkcs_req(WolfCertScepSession* s, const WolfCertScepCaps* caps, const uint8_t* ra_cert, size_t ra_cert_len, const uint8_t* ca_bundle, size_t ca_bundle_len, - const WolfCertKey* new_key, const uint8_t* csr_der, size_t csr_der_len, + const WolfCertKey* new_key, + const uint8_t* csr_der, size_t csr_der_len, WolfCertScepResult* out) { - if (out == NULL) - return WOLFCERT_ERR_BAD_ARG; - - /* Clearing the in-flight result would wipe the running request's. */ - if (s == NULL || !s->in_active || out != s->in_out) { - memset(out, 0, sizeof(*out)); - out->fail_info = -1; - } - - if (s == NULL || caps == NULL || ra_cert == NULL || ra_cert_len == 0 || - ca_bundle == NULL || ca_bundle_len == 0 || new_key == NULL || - csr_der == NULL || csr_der_len == 0) - return WOLFCERT_ERR_BAD_ARG; - - if (s->nonblocking) - return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", - "blocking call on an async session; use the _nb variant"); + ScepCall c = { .op = SCEP_OP_PKCS_REQ, .caps = caps, + .ra_cert = ra_cert, .ra_cert_len = ra_cert_len, + .ca_bundle = ca_bundle, .ca_bundle_len = ca_bundle_len, + .key = new_key, .csr = csr_der, .csr_len = csr_der_len, + .res_out = out }; - int rc = scep_session_begin_pkcs_req(s, caps, ra_cert, ra_cert_len, - ca_bundle, ca_bundle_len, - new_key, csr_der, csr_der_len, out); - if (rc != WOLFCERT_OK) - return rc; - return scep_session_drive_sync(s); + return scep_session_call(s, &c, 0); } int wolfcert_scep_session_pkcs_req_nb(WolfCertScepSession* s, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const WolfCertKey* new_key, const uint8_t* csr_der, size_t csr_der_len, - WolfCertScepResult* out) + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const WolfCertKey* new_key, + const uint8_t* csr_der, size_t csr_der_len, + WolfCertScepResult* out) { - if (out == NULL) - return WOLFCERT_ERR_BAD_ARG; + ScepCall c = { .op = SCEP_OP_PKCS_REQ, .caps = caps, + .ra_cert = ra_cert, .ra_cert_len = ra_cert_len, + .ca_bundle = ca_bundle, .ca_bundle_len = ca_bundle_len, + .key = new_key, .csr = csr_der, .csr_len = csr_der_len, + .res_out = out }; - if (s == NULL || !s->in_active || out != s->in_out) { - memset(out, 0, sizeof(*out)); - out->fail_info = -1; - } - - if (s == NULL) - return WOLFCERT_ERR_BAD_ARG; - - if (!s->nonblocking) - return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", - "non-blocking _nb call on a blocking session; use the blocking variant"); - - if (!s->in_active) { - if (caps == NULL || ra_cert == NULL || ra_cert_len == 0 || - ca_bundle == NULL || ca_bundle_len == 0 || new_key == NULL || - csr_der == NULL || csr_der_len == 0) - return WOLFCERT_ERR_BAD_ARG; - int rc = scep_session_begin_pkcs_req(s, caps, ra_cert, ra_cert_len, - ca_bundle, ca_bundle_len, - new_key, csr_der, csr_der_len, out); - if (rc != WOLFCERT_OK) - return rc; - } - else { - int rc = scep_session_resume_check(s, SCEP_SESS_OP_PKCS_REQ, out); - if (rc != WOLFCERT_OK) - return rc; - } - return scep_session_drive_nb(s); + return scep_session_call(s, &c, 1); } int wolfcert_scep_session_renewal_req(WolfCertScepSession* s, @@ -1853,77 +1473,37 @@ int wolfcert_scep_session_renewal_req(WolfCertScepSession* s, const uint8_t* ra_cert, size_t ra_cert_len, const uint8_t* ca_bundle, size_t ca_bundle_len, const uint8_t* current_cert, size_t current_cert_len, - const WolfCertKey* current_key, const uint8_t* csr_der, size_t csr_der_len, + const WolfCertKey* current_key, + const uint8_t* csr_der, size_t csr_der_len, WolfCertScepResult* out) { - if (out == NULL) - return WOLFCERT_ERR_BAD_ARG; - - if (s == NULL || !s->in_active || out != s->in_out) { - memset(out, 0, sizeof(*out)); - out->fail_info = -1; - } - - if (s == NULL || caps == NULL || ra_cert == NULL || ra_cert_len == 0 || - ca_bundle == NULL || ca_bundle_len == 0 || current_cert == NULL || - current_cert_len == 0 || current_key == NULL || csr_der == NULL || - csr_der_len == 0) - return WOLFCERT_ERR_BAD_ARG; - - if (s->nonblocking) - return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", - "blocking call on an async session; use the _nb variant"); - - int rc = scep_session_begin_renewal(s, caps, ra_cert, ra_cert_len, - ca_bundle, ca_bundle_len, - current_cert, current_cert_len, - current_key, csr_der, csr_der_len, out); - if (rc != WOLFCERT_OK) - return rc; - return scep_session_drive_sync(s); + ScepCall c = { .op = SCEP_OP_RENEWAL, .caps = caps, + .ra_cert = ra_cert, .ra_cert_len = ra_cert_len, + .ca_bundle = ca_bundle, .ca_bundle_len = ca_bundle_len, + .cert = current_cert, .cert_len = current_cert_len, + .key = current_key, .csr = csr_der, .csr_len = csr_der_len, + .res_out = out }; + + return scep_session_call(s, &c, 0); } int wolfcert_scep_session_renewal_req_nb(WolfCertScepSession* s, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const uint8_t* current_cert, size_t current_cert_len, - const WolfCertKey* current_key, const uint8_t* csr_der, size_t csr_der_len, - WolfCertScepResult* out) + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const uint8_t* current_cert, size_t current_cert_len, + const WolfCertKey* current_key, + const uint8_t* csr_der, size_t csr_der_len, + WolfCertScepResult* out) { - if (out == NULL) - return WOLFCERT_ERR_BAD_ARG; - - if (s == NULL || !s->in_active || out != s->in_out) { - memset(out, 0, sizeof(*out)); - out->fail_info = -1; - } - - if (s == NULL) - return WOLFCERT_ERR_BAD_ARG; - - if (!s->nonblocking) - return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", - "non-blocking _nb call on a blocking session; use the blocking variant"); - - if (!s->in_active) { - if (caps == NULL || ra_cert == NULL || ra_cert_len == 0 || ca_bundle == NULL || - ca_bundle_len == 0 || current_cert == NULL || current_cert_len == 0 || - current_key == NULL || csr_der == NULL || csr_der_len == 0) - return WOLFCERT_ERR_BAD_ARG; - int rc = scep_session_begin_renewal(s, caps, ra_cert, ra_cert_len, - ca_bundle, ca_bundle_len, - current_cert, current_cert_len, - current_key, csr_der, csr_der_len, out); - if (rc != WOLFCERT_OK) - return rc; - } - else { - int rc = scep_session_resume_check(s, SCEP_SESS_OP_RENEWAL, out); - if (rc != WOLFCERT_OK) - return rc; - } - return scep_session_drive_nb(s); + ScepCall c = { .op = SCEP_OP_RENEWAL, .caps = caps, + .ra_cert = ra_cert, .ra_cert_len = ra_cert_len, + .ca_bundle = ca_bundle, .ca_bundle_len = ca_bundle_len, + .cert = current_cert, .cert_len = current_cert_len, + .key = current_key, .csr = csr_der, .csr_len = csr_der_len, + .res_out = out }; + + return scep_session_call(s, &c, 1); } int wolfcert_scep_session_get_cert_initial(WolfCertScepSession* s, @@ -1936,72 +1516,206 @@ int wolfcert_scep_session_get_cert_initial(WolfCertScepSession* s, const uint8_t* transaction_id, size_t transaction_id_len, WolfCertScepResult* out) { - if (out == NULL) + ScepCall c = { .op = SCEP_OP_GET_CERT_INITIAL, .caps = caps, + .ra_cert = ra_cert, .ra_cert_len = ra_cert_len, + .ca_bundle = ca_bundle, .ca_bundle_len = ca_bundle_len, + .cert = signer_cert, .cert_len = signer_cert_len, + .key = signer_key, .csr = csr_der, .csr_len = csr_der_len, + .txid = transaction_id, .txid_len = transaction_id_len, + .res_out = out }; + + return scep_session_call(s, &c, 0); +} + +int wolfcert_scep_session_get_cert_initial_nb(WolfCertScepSession* s, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const uint8_t* signer_cert, size_t signer_cert_len, + const WolfCertKey* signer_key, + const uint8_t* csr_der, size_t csr_der_len, + const uint8_t* transaction_id, size_t transaction_id_len, + WolfCertScepResult* out) +{ + ScepCall c = { .op = SCEP_OP_GET_CERT_INITIAL, .caps = caps, + .ra_cert = ra_cert, .ra_cert_len = ra_cert_len, + .ca_bundle = ca_bundle, .ca_bundle_len = ca_bundle_len, + .cert = signer_cert, .cert_len = signer_cert_len, + .key = signer_key, .csr = csr_der, .csr_len = csr_der_len, + .txid = transaction_id, .txid_len = transaction_id_len, + .res_out = out }; + + return scep_session_call(s, &c, 1); +} + +int wolfcert_scep_session_get_cert(WolfCertScepSession* s, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const uint8_t* signer_cert, size_t signer_cert_len, + const WolfCertKey* signer_key, + const uint8_t* serial, size_t serial_len, + WolfCertScepResult* out) +{ + ScepCall c = { .op = SCEP_OP_GET_CERT, .caps = caps, + .ra_cert = ra_cert, .ra_cert_len = ra_cert_len, + .ca_bundle = ca_bundle, .ca_bundle_len = ca_bundle_len, + .cert = signer_cert, .cert_len = signer_cert_len, + .key = signer_key, .serial = serial, .serial_len = serial_len, + .res_out = out }; + + return scep_session_call(s, &c, 0); +} + +int wolfcert_scep_session_get_cert_nb(WolfCertScepSession* s, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const uint8_t* signer_cert, size_t signer_cert_len, + const WolfCertKey* signer_key, + const uint8_t* serial, size_t serial_len, + WolfCertScepResult* out) +{ + ScepCall c = { .op = SCEP_OP_GET_CERT, .caps = caps, + .ra_cert = ra_cert, .ra_cert_len = ra_cert_len, + .ca_bundle = ca_bundle, .ca_bundle_len = ca_bundle_len, + .cert = signer_cert, .cert_len = signer_cert_len, + .key = signer_key, .serial = serial, .serial_len = serial_len, + .res_out = out }; + + return scep_session_call(s, &c, 1); +} + +static int scep_one_shot(const WolfCertServerCfg* srv, const ScepCall* c) +{ + WolfCertScepSession* s = NULL; + int rc; + + if (scep_call_out(c) == NULL) return WOLFCERT_ERR_BAD_ARG; - if (s == NULL || !s->in_active || out != s->in_out) { - memset(out, 0, sizeof(*out)); - out->fail_info = -1; + scep_call_clear(NULL, c); + rc = scep_call_check(c); + if (rc == WOLFCERT_OK) + rc = scep_session_new(srv, &s); + if (rc == WOLFCERT_OK) + rc = scep_session_begin(s, c, 0); + if (rc == WOLFCERT_OK) + rc = scep_session_connect(s, srv); + if (rc == WOLFCERT_OK) { + wolfcert_http_session_set_close(s->http); + rc = scep_session_send(s, c, 0); } - if (s == NULL || caps == NULL || ra_cert == NULL || ra_cert_len == 0 || - ca_bundle == NULL || ca_bundle_len == 0 || signer_key == NULL || - csr_der == NULL || csr_der_len == 0 || transaction_id == NULL || - transaction_id_len == 0) - return WOLFCERT_ERR_BAD_ARG; + wolfcert_scep_session_close(s); + return rc; +} - if (s->nonblocking) - return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", - "blocking call on an async session; use the _nb variant"); +int wolfcert_scep_get_ca_caps(const WolfCertServerCfg* srv, WolfCertScepCaps* out) +{ + ScepCall c = { .op = SCEP_OP_GET_CA_CAPS, .caps_out = out }; - int rc = scep_session_begin_get_cert_initial(s, caps, ra_cert, ra_cert_len, - ca_bundle, ca_bundle_len, signer_cert, signer_cert_len, signer_key, - csr_der, csr_der_len, transaction_id, transaction_id_len, out); - if (rc != WOLFCERT_OK) - return rc; - return scep_session_drive_sync(s); + return scep_one_shot(srv, &c); } -int wolfcert_scep_session_get_cert_initial_nb(WolfCertScepSession* s, - const WolfCertScepCaps* caps, - const uint8_t* ra_cert, size_t ra_cert_len, - const uint8_t* ca_bundle, size_t ca_bundle_len, - const uint8_t* signer_cert, size_t signer_cert_len, - const WolfCertKey* signer_key, - const uint8_t* csr_der, size_t csr_der_len, - const uint8_t* transaction_id, size_t transaction_id_len, - WolfCertScepResult* out) +int wolfcert_scep_get_ca_cert(const WolfCertServerCfg* srv, WolfCertBuffer* out_ca_pem) { - if (out == NULL) - return WOLFCERT_ERR_BAD_ARG; + return wolfcert_scep_get_ca_cert_enc(srv, WOLFCERT_ENCODING_PEM, out_ca_pem); +} - if (s == NULL || !s->in_active || out != s->in_out) { - memset(out, 0, sizeof(*out)); - out->fail_info = -1; - } +int wolfcert_scep_get_ca_cert_enc(const WolfCertServerCfg* srv, WolfCertEncoding enc, + WolfCertBuffer* out_ca) +{ + ScepCall c = { .op = SCEP_OP_GET_CA_CERT, .enc = enc, .buf_out = out_ca }; - if (s == NULL) - return WOLFCERT_ERR_BAD_ARG; + return scep_one_shot(srv, &c); +} - if (!s->nonblocking) - return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", - "non-blocking _nb call on a blocking session; use the blocking variant"); - - if (!s->in_active) { - if (caps == NULL || ra_cert == NULL || ra_cert_len == 0 || ca_bundle == NULL || - ca_bundle_len == 0 || signer_key == NULL || csr_der == NULL || - csr_der_len == 0 || transaction_id == NULL || transaction_id_len == 0) - return WOLFCERT_ERR_BAD_ARG; - int rc = scep_session_begin_get_cert_initial(s, caps, ra_cert, ra_cert_len, - ca_bundle, ca_bundle_len, signer_cert, signer_cert_len, signer_key, - csr_der, csr_der_len, transaction_id, transaction_id_len, out); - if (rc != WOLFCERT_OK) - return rc; - } - else { - int rc = scep_session_resume_check(s, SCEP_SESS_OP_GET_CERT_INITIAL, out); - if (rc != WOLFCERT_OK) - return rc; - } - return scep_session_drive_nb(s); +int wolfcert_scep_get_next_ca_cert(const WolfCertServerCfg* srv, + const uint8_t* current_ca_der, + size_t current_ca_len, + WolfCertBuffer* out_next_ca_pem) +{ + ScepCall c = { .op = SCEP_OP_GET_NEXT_CA_CERT, + .ca_bundle = current_ca_der, .ca_bundle_len = current_ca_len, + .buf_out = out_next_ca_pem }; + + return scep_one_shot(srv, &c); +} + +int wolfcert_scep_pkcs_req(const WolfCertServerCfg* srv, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const WolfCertKey* new_key, + const uint8_t* csr_der, size_t csr_der_len, + WolfCertScepResult* out) +{ + ScepCall c = { .op = SCEP_OP_PKCS_REQ, .caps = caps, + .ra_cert = ra_cert, .ra_cert_len = ra_cert_len, + .ca_bundle = ca_bundle, .ca_bundle_len = ca_bundle_len, + .key = new_key, .csr = csr_der, .csr_len = csr_der_len, + .res_out = out }; + + return scep_one_shot(srv, &c); +} + +int wolfcert_scep_renewal_req(const WolfCertServerCfg* srv, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const uint8_t* current_cert, size_t current_cert_len, + const WolfCertKey* current_key, + const uint8_t* csr_der, size_t csr_der_len, + WolfCertScepResult* out) +{ + ScepCall c = { .op = SCEP_OP_RENEWAL, .caps = caps, + .ra_cert = ra_cert, .ra_cert_len = ra_cert_len, + .ca_bundle = ca_bundle, .ca_bundle_len = ca_bundle_len, + .cert = current_cert, .cert_len = current_cert_len, + .key = current_key, .csr = csr_der, .csr_len = csr_der_len, + .res_out = out }; + + return scep_one_shot(srv, &c); +} + +int wolfcert_scep_get_cert_initial(const WolfCertServerCfg* srv, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const uint8_t* signer_cert, size_t signer_cert_len, + const WolfCertKey* signer_key, + const uint8_t* csr_der, size_t csr_der_len, + const uint8_t* transaction_id, + size_t transaction_id_len, + WolfCertScepResult* out) +{ + ScepCall c = { .op = SCEP_OP_GET_CERT_INITIAL, .caps = caps, + .ra_cert = ra_cert, .ra_cert_len = ra_cert_len, + .ca_bundle = ca_bundle, .ca_bundle_len = ca_bundle_len, + .cert = signer_cert, .cert_len = signer_cert_len, + .key = signer_key, .csr = csr_der, .csr_len = csr_der_len, + .txid = transaction_id, .txid_len = transaction_id_len, + .res_out = out }; + + return scep_one_shot(srv, &c); +} + +int wolfcert_scep_get_cert(const WolfCertServerCfg* srv, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const uint8_t* signer_cert, size_t signer_cert_len, + const WolfCertKey* signer_key, + const uint8_t* serial, size_t serial_len, + WolfCertScepResult* out) +{ + ScepCall c = { .op = SCEP_OP_GET_CERT, .caps = caps, + .ra_cert = ra_cert, .ra_cert_len = ra_cert_len, + .ca_bundle = ca_bundle, .ca_bundle_len = ca_bundle_len, + .cert = signer_cert, .cert_len = signer_cert_len, + .key = signer_key, .serial = serial, .serial_len = serial_len, + .res_out = out }; + + return scep_one_shot(srv, &c); } diff --git a/tests/integration/test_est_pending_roundtrip.c b/tests/integration/test_est_pending_roundtrip.c index 338194b..fd7fa59 100644 --- a/tests/integration/test_est_pending_roundtrip.c +++ b/tests/integration/test_est_pending_roundtrip.c @@ -360,6 +360,14 @@ static int pending_path(WolfCertServer* s) == WOLFCERT_ERR_BAD_ARG); REQUIRE(other_ca.data == NULL); + /* A blocking call with the in-flight output is refused too, and keeps + * the request: another operation still gets BAD_ARG. */ + REQUIRE(wolfcert_est_session_get_cacerts(msess, &mix_ca) + == WOLFCERT_ERR_BAD_ARG); + REQUIRE(wolfcert_est_session_simple_enroll_nb(msess, csr_mix.data, + csr_mix.len, &mr) + == WOLFCERT_ERR_BAD_ARG); + /* The refused calls left the /cacerts request in flight. */ rc = wolfcert_est_session_get_cacerts_nb(msess, &mix_ca); REQUIRE(rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE); diff --git a/tests/integration/test_scep_async_roundtrip.c b/tests/integration/test_scep_async_roundtrip.c index 633c8af..86231c3 100644 --- a/tests/integration/test_scep_async_roundtrip.c +++ b/tests/integration/test_scep_async_roundtrip.c @@ -272,7 +272,7 @@ static int async_enroll_path(WolfCertServer* s) REQUIRE_CLEAN(bootstrap(&cli, "CN=async-scep-1", &caps, &ca_pem, &ca_der, &dk, &csr) == 0); - REQUIRE_CLEAN(wolfcert_scep_session_open_async(&cli, &sess) == WOLFCERT_OK); + REQUIRE_CLEAN(wolfcert_scep_session_open(&cli, &sess) == WOLFCERT_OK); REQUIRE_CLEAN(wolfcert_scep_session_fd(sess) >= 0); REQUIRE_CLEAN(pump_pkcs_req(sess, &caps, ca_der->buffer, ca_der->length, @@ -359,7 +359,7 @@ static int async_poll_path(WolfCertServer* s) REQUIRE_CLEAN(bootstrap(&cli, "CN=async-scep-poll", &caps, &ca_pem, &ca_der, &dk, &csr) == 0); - REQUIRE_CLEAN(wolfcert_scep_session_open_async(&cli, &sess) == WOLFCERT_OK); + REQUIRE_CLEAN(wolfcert_scep_session_open(&cli, &sess) == WOLFCERT_OK); /* First round trip: PENDING with a transactionID. */ REQUIRE_CLEAN(pump_pkcs_req(sess, &caps, ca_der->buffer, ca_der->length, @@ -403,6 +403,9 @@ static int async_renewal_path(WolfCertServer* s) WolfCertScepResult r1 = { 0 }; DerBuffer* cur_der = NULL; WolfCertScepResult r2 = { 0 }; + uint8_t* ca_copy = NULL; + uint8_t* cert_copy = NULL; + int rc; int ret = 1; snprintf(url, sizeof(url), "http://127.0.0.1:%u/scep", wolfcert_server_port(s)); @@ -411,7 +414,7 @@ static int async_renewal_path(WolfCertServer* s) REQUIRE_CLEAN(bootstrap(&cli, "CN=async-scep-renew", &caps, &ca_pem, &ca_der, &dk, &csr) == 0); - REQUIRE_CLEAN(wolfcert_scep_session_open_async(&cli, &sess) == WOLFCERT_OK); + REQUIRE_CLEAN(wolfcert_scep_session_open(&cli, &sess) == WOLFCERT_OK); /* First round trip: enroll to obtain the cert to renew. */ REQUIRE_CLEAN(pump_pkcs_req(sess, &caps, ca_der->buffer, ca_der->length, @@ -421,7 +424,24 @@ static int async_renewal_path(WolfCertServer* s) REQUIRE_CLEAN(wc_PemToDer(r1.cert_pem.data, (long)r1.cert_pem.len, CERT_TYPE, &cur_der, NULL, NULL, NULL) == 0); - /* Second round trip: RenewalReq signed by the cert just issued. */ + /* Second round trip: RenewalReq signed by the cert just issued. The + * session keeps its own copies, so the caller's CA bundle and cert may go + * once the request is in flight. */ + ca_copy = (uint8_t*)malloc(ca_der->length); + cert_copy = (uint8_t*)malloc(cur_der->length); + REQUIRE_CLEAN(ca_copy != NULL && cert_copy != NULL); + memcpy(ca_copy, ca_der->buffer, ca_der->length); + memcpy(cert_copy, cur_der->buffer, cur_der->length); + rc = wolfcert_scep_session_renewal_req_nb(sess, &caps, ca_copy, + ca_der->length, ca_copy, ca_der->length, cert_copy, + cur_der->length, dk, csr.data, csr.len, &r2); + REQUIRE_CLEAN(rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE); + memset(ca_copy, 0, ca_der->length); + memset(cert_copy, 0, cur_der->length); + free(ca_copy); + free(cert_copy); + ca_copy = NULL; + cert_copy = NULL; REQUIRE_CLEAN(pump_renewal_req(sess, &caps, ca_der->buffer, ca_der->length, cur_der->buffer, cur_der->length, dk, csr.data, csr.len, &r2) == 0); @@ -434,6 +454,8 @@ static int async_renewal_path(WolfCertServer* s) wolfcert_scep_session_close(sess); wolfcert_scep_result_free(&r1); wolfcert_scep_result_free(&r2); + free(ca_copy); + free(cert_copy); if (cur_der != NULL) wc_FreeDer(&cur_der); if (ca_der != NULL) @@ -458,9 +480,7 @@ static int async_guard_path(WolfCertServer* s) DerBuffer* ca_der = NULL; WolfCertKey* dk = NULL; WolfCertBuffer csr = { 0 }; - WolfCertScepSession* bsess = NULL; WolfCertScepSession* asess = NULL; - WolfCertScepResult rbad = { 0 }; WolfCertScepResult r1 = { 0 }; WolfCertScepResult r2 = { 0 }; uint16_t bh_port = 0; @@ -481,19 +501,7 @@ static int async_guard_path(WolfCertServer* s) snprintf(bh_url, sizeof(bh_url), "http://127.0.0.1:%u/scep", (unsigned)bh_port); cli = (WolfCertServerCfg){ .protocol = WOLFCERT_PROTO_SCEP, .server_url = bh_url }; - /* Mode guard: an _nb call on a blocking session is rejected. */ - REQUIRE_CLEAN(wolfcert_scep_session_open(&cli, &bsess) == WOLFCERT_OK); - REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req_nb(bsess, &caps, - ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, - dk, csr.data, csr.len, &rbad) == WOLFCERT_ERR_BAD_ARG); - wolfcert_scep_session_close(bsess); - bsess = NULL; - - /* Mode guard: an _ex call on an async session is rejected. */ - REQUIRE_CLEAN(wolfcert_scep_session_open_async(&cli, &asess) == WOLFCERT_OK); - REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(asess, &caps, - ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, - dk, csr.data, csr.len, &rbad) == WOLFCERT_ERR_BAD_ARG); + REQUIRE_CLEAN(wolfcert_scep_session_open(&cli, &asess) == WOLFCERT_OK); /* Leave a PKCSReq to the black hole in flight for the guard checks. */ rc = wolfcert_scep_session_pkcs_req_nb(asess, &caps, @@ -533,23 +541,28 @@ static int async_guard_path(WolfCertServer* s) REQUIRE_CLEAN(rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE); REQUIRE_CLEAN(r1.fail_info == 42); - /* An _ex call rejected on an async session leaves the result intact. */ + /* A blocking call while the request is in flight is rejected and leaves + * the result intact. */ REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(asess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, dk, csr.data, csr.len, &r1) == WOLFCERT_ERR_BAD_ARG); REQUIRE_CLEAN(r1.fail_info == 42); + /* The _nb request is still in flight after the rejected blocking call. */ + rc = wolfcert_scep_session_pkcs_req_nb(asess, &caps, + ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, + dk, csr.data, csr.len, &r1); + REQUIRE_CLEAN(rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE); + REQUIRE_CLEAN(r1.fail_info == 42); + ret = 0; cleanup: - if (bsess != NULL) - wolfcert_scep_session_close(bsess); if (asess != NULL) wolfcert_scep_session_close(asess); if (bh_fd >= 0) close(bh_fd); wolfcert_scep_result_free(&r1); wolfcert_scep_result_free(&r2); - wolfcert_scep_result_free(&rbad); if (ca_der != NULL) wc_FreeDer(&ca_der); wolfcert_buffer_free(&ca_pem); @@ -684,16 +697,13 @@ static int tls_guard_path(void) REQUIRE(wolfcert_scep_session_open(&cli, &sess) == WOLFCERT_ERR_TLS); REQUIRE(sess == NULL); - REQUIRE(wolfcert_scep_session_open_async(&cli, &sess) == WOLFCERT_ERR_TLS); - REQUIRE(sess == NULL); /* NULL-input contract of the session accessors. */ REQUIRE(wolfcert_scep_session_fd(NULL) == -1); wolfcert_scep_session_close(NULL); /* no-op, must not crash */ - /* NULL srv / server_url are rejected by both opens. */ + /* NULL srv / server_url are rejected by the open. */ REQUIRE(wolfcert_scep_session_open(NULL, &sess) == WOLFCERT_ERR_BAD_ARG); - REQUIRE(wolfcert_scep_session_open_async(NULL, &sess) == WOLFCERT_ERR_BAD_ARG); WolfCertServerCfg no_url = { .protocol = WOLFCERT_PROTO_SCEP, .server_url = NULL }; REQUIRE(wolfcert_scep_session_open(&no_url, &sess) == WOLFCERT_ERR_BAD_ARG); return 0; @@ -732,7 +742,7 @@ static int non_rsa_reject_path(WolfCertServer* s) == WOLFCERT_ERR_UNSUPPORTED); REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, - NULL, 0, ek, csr.data, csr.len, csr.data, csr.len, &r) + NULL, 0, ek, csr.data, csr.len, (const uint8_t*)"TXID", 4, &r) == WOLFCERT_ERR_UNSUPPORTED); ret = 0; @@ -1024,7 +1034,7 @@ static int negative_args_path(WolfCertServer* s) ca = ca_der->buffer; ca_len = ca_der->length; - /* PKCSReq: NULL session, NULL out, NULL/zero required arg, NULL caps. */ + /* PKCSReq: NULL session, NULL out, NULL/zero required arg. */ REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(NULL, &caps, ca, ca_len, ca, ca_len, dk, csr.data, csr.len, &r) == WOLFCERT_ERR_BAD_ARG); REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(sess, &caps, ca, ca_len, @@ -1033,8 +1043,19 @@ static int negative_args_path(WolfCertServer* s) ca, ca_len, dk, csr.data, csr.len, &r) == WOLFCERT_ERR_BAD_ARG); REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(sess, &caps, ca, 0, ca, ca_len, dk, csr.data, csr.len, &r) == WOLFCERT_ERR_BAD_ARG); + + /* NULL caps is accepted: POST, and the 3DES fallback without "AES", + * which a wolfSSL without 3DES refuses. */ +#ifndef NO_DES3 REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(sess, NULL, ca, ca_len, - ca, ca_len, dk, csr.data, csr.len, &r) == WOLFCERT_ERR_BAD_ARG); + ca, ca_len, dk, csr.data, csr.len, &r) == WOLFCERT_OK); + REQUIRE_CLEAN(r.status == WOLFCERT_SCEP_STATUS_SUCCESS); + wolfcert_scep_result_free(&r); +#else + REQUIRE_CLEAN(wolfcert_scep_session_pkcs_req(sess, NULL, ca, ca_len, + ca, ca_len, dk, csr.data, csr.len, &r) + == WOLFCERT_ERR_UNSUPPORTED); +#endif /* RenewalReq: a NULL op-specific required arg (current_cert). */ REQUIRE_CLEAN(wolfcert_scep_session_renewal_req(sess, &caps, ca, ca_len, @@ -1045,13 +1066,6 @@ static int negative_args_path(WolfCertServer* s) ca, ca_len, NULL, 0, dk, csr.data, csr.len, NULL, 0, &r) == WOLFCERT_ERR_BAD_ARG); - /* Mode guard: _nb on a blocking session is rejected. */ - REQUIRE_CLEAN(wolfcert_scep_session_renewal_req_nb(sess, &caps, ca, ca_len, - ca, ca_len, ca, ca_len, dk, csr.data, csr.len, &r) == WOLFCERT_ERR_BAD_ARG); - REQUIRE_CLEAN(wolfcert_scep_session_get_cert_initial_nb(sess, &caps, ca, ca_len, - ca, ca_len, NULL, 0, dk, csr.data, csr.len, csr.data, csr.len, &r) - == WOLFCERT_ERR_BAD_ARG); - ret = 0; cleanup: if (sess != NULL) @@ -1066,6 +1080,129 @@ static int negative_args_path(WolfCertServer* s) return ret; } +/* Scenario L: GetCACaps, GetCACert, GetNextCACert and GetCert on one session, + * blocking and then _nb, each returning what the one-shot returns. */ +static int session_get_ops_path(WolfCertServer* s) +{ + char url[128]; + WolfCertServerCfg cli; + WolfCertScepCaps caps = { 0 }; + WolfCertScepCaps scaps = { 0 }; + WolfCertBuffer ca_pem = { 0 }; + DerBuffer* ca_der = NULL; + WolfCertKey* dk = NULL; + WolfCertBuffer csr = { 0 }; + WolfCertBuffer next = { 0 }; + WolfCertBuffer sca = { 0 }; + WolfCertBuffer snext = { 0 }; + WolfCertScepSession* sess = NULL; + WolfCertScepResult issued = { 0 }; + WolfCertScepResult got = { 0 }; + DerBuffer* issued_der = NULL; + DecodedCert dc; + int parsed = 0; + int fd; + int rc; + int ret = 1; + + snprintf(url, sizeof(url), "http://127.0.0.1:%u/scep", wolfcert_server_port(s)); + cli = (WolfCertServerCfg){ .protocol = WOLFCERT_PROTO_SCEP, .server_url = url }; + REQUIRE_CLEAN(bootstrap(&cli, "CN=session-get-ops", &caps, &ca_pem, &ca_der, + &dk, &csr) == 0); + REQUIRE_CLEAN(wolfcert_scep_get_next_ca_cert(&cli, ca_der->buffer, + ca_der->length, &next) == WOLFCERT_OK); + + /* A cert to fetch back by its serial. */ + REQUIRE_CLEAN(wolfcert_scep_pkcs_req(&cli, &caps, ca_der->buffer, + ca_der->length, ca_der->buffer, ca_der->length, dk, csr.data, + csr.len, &issued) == WOLFCERT_OK); + REQUIRE_CLEAN(issued.status == WOLFCERT_SCEP_STATUS_SUCCESS); + REQUIRE_CLEAN(wc_PemToDer(issued.cert_pem.data, (long)issued.cert_pem.len, + CERT_TYPE, &issued_der, NULL, NULL, NULL) == 0); + wc_InitDecodedCert(&dc, issued_der->buffer, issued_der->length, NULL); + parsed = 1; + REQUIRE_CLEAN(wc_ParseCert(&dc, CERT_TYPE, NO_VERIFY, NULL) == 0); + + REQUIRE_CLEAN(wolfcert_scep_session_open(&cli, &sess) == WOLFCERT_OK); + REQUIRE_CLEAN(wolfcert_scep_session_get_ca_caps(sess, &scaps) == WOLFCERT_OK); + REQUIRE_CLEAN(memcmp(&scaps, &caps, sizeof(caps)) == 0); + REQUIRE_CLEAN(wolfcert_scep_session_get_ca_cert_enc(sess, + WOLFCERT_ENCODING_DER, &sca) == WOLFCERT_OK); + REQUIRE_CLEAN(sca.len == ca_der->length && + memcmp(sca.data, ca_der->buffer, sca.len) == 0); + REQUIRE_CLEAN(wolfcert_scep_session_get_next_ca_cert(sess, ca_der->buffer, + ca_der->length, &snext) == WOLFCERT_OK); + REQUIRE_CLEAN(snext.len == next.len && + memcmp(snext.data, next.data, next.len) == 0); + REQUIRE_CLEAN(wolfcert_scep_session_get_cert(sess, &caps, ca_der->buffer, + ca_der->length, ca_der->buffer, ca_der->length, + issued_der->buffer, issued_der->length, dk, dc.serial, + (size_t)dc.serialSz, &got) == WOLFCERT_OK); + REQUIRE_CLEAN(got.status == WOLFCERT_SCEP_STATUS_SUCCESS); + REQUIRE_CLEAN(memmem(got.cert_pem.data, got.cert_pem.len, + issued.cert_pem.data, issued.cert_pem.len) != NULL); + memset(&scaps, 0, sizeof(scaps)); + wolfcert_buffer_free(&sca); + wolfcert_buffer_free(&snext); + wolfcert_scep_result_free(&got); + + fd = wolfcert_scep_session_fd(sess); + do { + rc = wolfcert_scep_session_get_ca_caps_nb(sess, &scaps); + } while ((rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE) && + wait_ready(fd, rc) == 0); + REQUIRE_CLEAN(rc == WOLFCERT_OK); + REQUIRE_CLEAN(memcmp(&scaps, &caps, sizeof(caps)) == 0); + do { + rc = wolfcert_scep_session_get_ca_cert_enc_nb(sess, + WOLFCERT_ENCODING_DER, &sca); + } while ((rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE) && + wait_ready(fd, rc) == 0); + REQUIRE_CLEAN(rc == WOLFCERT_OK); + REQUIRE_CLEAN(sca.len == ca_der->length && + memcmp(sca.data, ca_der->buffer, sca.len) == 0); + do { + rc = wolfcert_scep_session_get_next_ca_cert_nb(sess, ca_der->buffer, + ca_der->length, &snext); + } while ((rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE) && + wait_ready(fd, rc) == 0); + REQUIRE_CLEAN(rc == WOLFCERT_OK); + REQUIRE_CLEAN(snext.len == next.len && + memcmp(snext.data, next.data, next.len) == 0); + do { + rc = wolfcert_scep_session_get_cert_nb(sess, &caps, ca_der->buffer, + ca_der->length, ca_der->buffer, ca_der->length, + issued_der->buffer, issued_der->length, dk, dc.serial, + (size_t)dc.serialSz, &got); + } while ((rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE) && + wait_ready(fd, rc) == 0); + REQUIRE_CLEAN(rc == WOLFCERT_OK); + REQUIRE_CLEAN(got.status == WOLFCERT_SCEP_STATUS_SUCCESS); + REQUIRE_CLEAN(memmem(got.cert_pem.data, got.cert_pem.len, + issued.cert_pem.data, issued.cert_pem.len) != NULL); + + ret = 0; +cleanup: + if (sess != NULL) + wolfcert_scep_session_close(sess); + if (parsed) + wc_FreeDecodedCert(&dc); + if (issued_der != NULL) + wc_FreeDer(&issued_der); + wolfcert_scep_result_free(&got); + wolfcert_scep_result_free(&issued); + wolfcert_buffer_free(&snext); + wolfcert_buffer_free(&sca); + wolfcert_buffer_free(&next); + if (ca_der != NULL) + wc_FreeDer(&ca_der); + wolfcert_buffer_free(&ca_pem); + wolfcert_buffer_free(&csr); + if (dk != NULL) + wolfcert_key_free(dk); + return ret; +} + int main(void) { REQUIRE(wolfcert_init(NULL) == WOLFCERT_OK); @@ -1229,6 +1366,24 @@ int main(void) if (rc != 0) return rc; + /* Scenario L: roll-over and GetCert enabled, the GET operations and + * GetCert over a session. */ + WolfCertServerCfgSrv cfg_get_ops = { .protocol = WOLFCERT_PROTO_SCEP, + .bind_host = "127.0.0.1", .bind_port = 0, + .ca_store = test_ca_store(), + .scep_enable_next_ca = 1, + .scep_enable_get_cert = 1 }; + WolfCertServer* s11 = NULL; + REQUIRE(wolfcert_server_start(&cfg_get_ops, &s11) == WOLFCERT_OK); + pthread_t t11; + REQUIRE(pthread_create(&t11, NULL, server_thread, s11) == 0); + rc = session_get_ops_path(s11); + wolfcert_server_stop(s11); + pthread_join(t11, NULL); + wolfcert_server_free(s11); + if (rc != 0) + return rc; + test_ca_store_close(); wolfcert_cleanup(); printf("OK\n"); diff --git a/tests/integration/test_scep_roundtrip.c b/tests/integration/test_scep_roundtrip.c index 56031e8..4341cda 100644 --- a/tests/integration/test_scep_roundtrip.c +++ b/tests/integration/test_scep_roundtrip.c @@ -581,6 +581,7 @@ struct canned_ctx { const char* content_type; const uint8_t* body; size_t body_len; + int saw_close; /* the request sent Connection: close */ }; /* Bind a loopback listener and report its ephemeral port. */ @@ -630,6 +631,8 @@ static void* canned_srv_thread(void* arg) if (strstr(buf, "\r\n\r\n") != NULL) break; } + buf[n] = '\0'; + cc->saw_close = strstr(buf, "Connection: close\r\n") != NULL; char head[256]; int hn = snprintf(head, sizeof(head), @@ -709,6 +712,7 @@ static int test_caps_scep_standard(void) int rc = wolfcert_scep_get_ca_caps(&cli, &caps); pthread_join(tid, NULL); REQUIRE(rc == WOLFCERT_OK); + REQUIRE(cc.saw_close == 1); REQUIRE(caps.scep_standard == 1); REQUIRE(caps.post_pki_operation == 1); diff --git a/tests/unit/test_http.c b/tests/unit/test_http.c index d50a93e..34f9d3f 100644 --- a/tests/unit/test_http.c +++ b/tests/unit/test_http.c @@ -120,62 +120,6 @@ static int test_url_parser(void) return 0; } -static int test_url_origin(void) -{ - WolfCertUrl u; - char* origin = NULL; - - /* Default ports (https:443, http:80) are omitted. */ - REQUIRE(wolfcert_http_url_parse("https://ca.example.com/scep", &u, NULL) == WOLFCERT_OK); - REQUIRE(wolfcert_http_url_origin(&u, NULL, &origin) == WOLFCERT_OK); - REQUIRE(strcmp(origin, "https://ca.example.com") == 0); - WOLFCERT_XFREE(origin, NULL); origin = NULL; - wolfcert_http_url_free(&u); - - REQUIRE(wolfcert_http_url_parse("http://host.example/x", &u, NULL) == WOLFCERT_OK); - REQUIRE(wolfcert_http_url_origin(&u, NULL, &origin) == WOLFCERT_OK); - REQUIRE(strcmp(origin, "http://host.example") == 0); - WOLFCERT_XFREE(origin, NULL); origin = NULL; - wolfcert_http_url_free(&u); - - /* A non-default port is included. */ - REQUIRE(wolfcert_http_url_parse("http://host.example:8080/x", &u, NULL) == WOLFCERT_OK); - REQUIRE(wolfcert_http_url_origin(&u, NULL, &origin) == WOLFCERT_OK); - REQUIRE(strcmp(origin, "http://host.example:8080") == 0); - WOLFCERT_XFREE(origin, NULL); origin = NULL; - wolfcert_http_url_free(&u); - - /* An IPv6 literal is re-bracketed so the origin parses again. */ - REQUIRE(wolfcert_http_url_parse("https://[::1]:8443/p", &u, NULL) == WOLFCERT_OK); - REQUIRE(wolfcert_http_url_origin(&u, NULL, &origin) == WOLFCERT_OK); - REQUIRE(strcmp(origin, "https://[::1]:8443") == 0); - wolfcert_http_url_free(&u); - REQUIRE(wolfcert_http_url_parse(origin, &u, NULL) == WOLFCERT_OK); - REQUIRE(strcmp(u.host, "::1") == 0); - REQUIRE(u.port == 8443); - WOLFCERT_XFREE(origin, NULL); origin = NULL; - wolfcert_http_url_free(&u); - - /* Same for the default port, where no ":port" suffix follows the host. */ - REQUIRE(wolfcert_http_url_parse("https://[2001:db8::1]/p", &u, NULL) == WOLFCERT_OK); - REQUIRE(wolfcert_http_url_origin(&u, NULL, &origin) == WOLFCERT_OK); - REQUIRE(strcmp(origin, "https://[2001:db8::1]") == 0); - wolfcert_http_url_free(&u); - REQUIRE(wolfcert_http_url_parse(origin, &u, NULL) == WOLFCERT_OK); - REQUIRE(strcmp(u.host, "2001:db8::1") == 0); - REQUIRE(u.port == 443); - WOLFCERT_XFREE(origin, NULL); origin = NULL; - wolfcert_http_url_free(&u); - - /* NULL url and NULL out are rejected. */ - REQUIRE(wolfcert_http_url_origin(NULL, NULL, &origin) == WOLFCERT_ERR_BAD_ARG); - REQUIRE(wolfcert_http_url_parse("https://h/x", &u, NULL) == WOLFCERT_OK); - REQUIRE(wolfcert_http_url_origin(&u, NULL, NULL) == WOLFCERT_ERR_BAD_ARG); - wolfcert_http_url_free(&u); - - return 0; -} - struct srv_ctx { int listen_fd; }; /* Returns a bound loopback listener and its ephemeral port. */ @@ -1273,8 +1217,6 @@ int main(void) if (test_server_read_chunked_caps()) return 1; #endif - if (test_url_origin()) - return 1; if (test_chunked_complete_resume()) return 1; if (test_loopback_http()) diff --git a/tests/unit/test_scep_msg.c b/tests/unit/test_scep_msg.c index 948e382..ea5373a 100644 --- a/tests/unit/test_scep_msg.c +++ b/tests/unit/test_scep_msg.c @@ -997,6 +997,7 @@ static int test_zero_length_args_rejected(void) WolfCertServerCfg srv = { .protocol = WOLFCERT_PROTO_SCEP, .server_url = "http://127.0.0.1:1/scep" }; uint8_t blob[4] = { 1, 2, 3, 4 }; + uint8_t txid[4] = { 'T', 'X', 'I', 'D' }; WolfCertKey* key = NULL; REQUIRE(wolfcert_key_from_pem(client_key_der_2048, @@ -1044,22 +1045,28 @@ static int test_zero_length_args_rejected(void) wolfcert_scep_get_cert_initial(&srv, &caps, blob, 0, blob, sizeof(blob), blob, sizeof(blob), key, blob, sizeof(blob), - blob, sizeof(blob), &r)); + txid, sizeof(txid), &r)); REJECTS("get_cert_initial ca_bundle_len", wolfcert_scep_get_cert_initial(&srv, &caps, blob, sizeof(blob), blob, 0, blob, sizeof(blob), key, blob, sizeof(blob), - blob, sizeof(blob), &r)); + txid, sizeof(txid), &r)); REJECTS("get_cert_initial csr_der_len", wolfcert_scep_get_cert_initial(&srv, &caps, blob, sizeof(blob), blob, sizeof(blob), blob, sizeof(blob), key, blob, 0, - blob, sizeof(blob), &r)); + txid, sizeof(txid), &r)); /* signer_cert stays optional, but a non-NULL one must carry bytes. */ REJECTS("get_cert_initial signer_cert_len", wolfcert_scep_get_cert_initial(&srv, &caps, blob, sizeof(blob), blob, sizeof(blob), blob, 0, key, blob, sizeof(blob), + txid, sizeof(txid), &r)); + /* A transactionID outside PrintableString is refused before the dial. */ + REJECTS("get_cert_initial transaction_id", + wolfcert_scep_get_cert_initial(&srv, &caps, blob, sizeof(blob), + blob, sizeof(blob), NULL, 0, + key, blob, sizeof(blob), blob, sizeof(blob), &r)); #undef REJECTS @@ -2009,8 +2016,30 @@ static int test_getca_url(void) return 0; } +/* A PKIOperation one-shot builds its request before it dials, so a request + * that cannot be built fails locally rather than as an I/O error. */ +static int test_scep_builds_before_dial(void) +{ + WolfCertServerCfg srv = { .protocol = WOLFCERT_PROTO_SCEP, + .server_url = "http://127.0.0.1:1/scep" }; + uint8_t blob[4] = { 1, 2, 3, 4 }; + WolfCertScepResult r; + WolfCertKey* key = NULL; + int rc; + + REQUIRE(wolfcert_key_from_pem(client_key_der_2048, + sizeof(client_key_der_2048), NULL, &key) + == WOLFCERT_OK); + rc = wolfcert_scep_pkcs_req(&srv, NULL, blob, sizeof(blob), + blob, sizeof(blob), key, blob, sizeof(blob), &r); + wolfcert_key_free(key); + REQUIRE(rc != WOLFCERT_OK && rc != WOLFCERT_ERR_IO); + wolfcert_scep_result_free(&r); + return 0; +} + /* An EST config with Basic credentials gets BAD_ARG from the SCEP GetCA* - * calls and both session opens. */ + * calls and the session open. */ static int test_scep_rejects_est_cfg(void) { static const uint8_t dummy_ca[] = { 0x30, 0x03, 0x02, 0x01, 0x00 }; @@ -2033,8 +2062,6 @@ static int test_scep_rejects_est_cfg(void) REQUIRE(wolfcert_scep_session_open(&srv, &sess) == WOLFCERT_ERR_BAD_ARG); REQUIRE(sess == NULL); - REQUIRE(wolfcert_scep_session_open_async(&srv, &sess) == WOLFCERT_ERR_BAD_ARG); - REQUIRE(sess == NULL); /* protocol = 0 is refused too. */ srv.protocol = (WolfCertProtocol)0; @@ -2859,6 +2886,8 @@ int main(void) REQUIRE(wolfcert_init(test_heap_hint()) == WOLFCERT_OK); if (run_test(test_getca_url)) return 1; + if (run_test(test_scep_builds_before_dial)) + return 1; if (run_test(test_scep_rejects_est_cfg)) return 1; if (run_test(test_envelop_cipher_oid)) diff --git a/wolfcert/scep.h b/wolfcert/scep.h index 124d888..024d175 100644 --- a/wolfcert/scep.h +++ b/wolfcert/scep.h @@ -203,26 +203,41 @@ WOLFCERT_API int wolfcert_scep_get_next_ca_cert(const WolfCertServerCfg* srv, /* Keep-alive SCEP session carrying several PKIOperation round trips; GetCACaps * and GetCACert stay one-shot calls. Plain http:// is accepted and https:// - * needs srv->verify_server. The blocking calls need wolfcert_scep_session_open - * and the _nb calls wolfcert_scep_session_open_async; a mismatch returns - * WOLFCERT_ERR_BAD_ARG. A _nb call returning WANT_READ / WANT_WRITE is repeated - * with the same arguments; a call with a different out pointer returns - * WOLFCERT_ERR_BAD_ARG. DNS and the initial connect stay synchronous. Over - * https:// open does not verify the server: the first request runs the TLS - * handshake and returns WOLFCERT_ERR_TLS if it fails, or WOLFCERT_ERR_MEMORY - * if wolfSSL runs out of memory. */ + * needs srv->verify_server. A _nb call returning WANT_READ / WANT_WRITE is + * repeated with the same arguments; a call with a different out pointer + * returns WOLFCERT_ERR_BAD_ARG. DNS and the initial connect stay synchronous. + * Over https:// open does not verify the server: the first request runs the + * TLS handshake and returns WOLFCERT_ERR_TLS if it fails, or + * WOLFCERT_ERR_MEMORY if wolfSSL runs out of memory. */ typedef struct WolfCertScepSession WolfCertScepSession; WOLFCERT_API int wolfcert_scep_session_open(const WolfCertServerCfg* srv, WolfCertScepSession** out); -WOLFCERT_API int wolfcert_scep_session_open_async(const WolfCertServerCfg* srv, - WolfCertScepSession** out); WOLFCERT_API void wolfcert_scep_session_close(WolfCertScepSession* s); /* Socket fd of the backing HTTP session - hand to poll/epoll/kqueue; * -1 when a caller-supplied WolfCertTransport backs it (no descriptor). */ WOLFCERT_API int wolfcert_scep_session_fd(const WolfCertScepSession* s); +WOLFCERT_API int wolfcert_scep_session_get_ca_caps(WolfCertScepSession* s, + WolfCertScepCaps* out_caps); +WOLFCERT_API int wolfcert_scep_session_get_ca_caps_nb(WolfCertScepSession* s, + WolfCertScepCaps* out_caps); +WOLFCERT_API int wolfcert_scep_session_get_ca_cert_enc(WolfCertScepSession* s, + WolfCertEncoding enc, + WolfCertBuffer* out_ca); +WOLFCERT_API int wolfcert_scep_session_get_ca_cert_enc_nb(WolfCertScepSession* s, + WolfCertEncoding enc, + WolfCertBuffer* out_ca); +WOLFCERT_API int wolfcert_scep_session_get_next_ca_cert(WolfCertScepSession* s, + const uint8_t* current_ca_der, + size_t current_ca_len, + WolfCertBuffer* out_next_ca_pem); +WOLFCERT_API int wolfcert_scep_session_get_next_ca_cert_nb(WolfCertScepSession* s, + const uint8_t* current_ca_der, + size_t current_ca_len, + WolfCertBuffer* out_next_ca_pem); + /* PKCSReq over the session; arguments and result as for * wolfcert_scep_pkcs_req. */ WOLFCERT_API int wolfcert_scep_session_pkcs_req(WolfCertScepSession* s, @@ -274,6 +289,23 @@ WOLFCERT_API int wolfcert_scep_session_get_cert_initial_nb(WolfCertScepSession* const uint8_t* transaction_id, size_t transaction_id_len, WolfCertScepResult* out); +WOLFCERT_API int wolfcert_scep_session_get_cert(WolfCertScepSession* s, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const uint8_t* signer_cert, size_t signer_cert_len, + const WolfCertKey* signer_key, + const uint8_t* serial, size_t serial_len, + WolfCertScepResult* out); +WOLFCERT_API int wolfcert_scep_session_get_cert_nb(WolfCertScepSession* s, + const WolfCertScepCaps* caps, + const uint8_t* ra_cert, size_t ra_cert_len, + const uint8_t* ca_bundle, size_t ca_bundle_len, + const uint8_t* signer_cert, size_t signer_cert_len, + const WolfCertKey* signer_key, + const uint8_t* serial, size_t serial_len, + WolfCertScepResult* out); + #ifdef __cplusplus } #endif From 311ade8509ed1205e0b53ba332d90ed79f8f8d83 Mon Sep 17 00:00:00 2001 From: Yosuke Shimizu Date: Wed, 21 Oct 2026 08:15:27 +0900 Subject: [PATCH 5/5] Take the encoding as an argument; describe one session type in the docs - The EST cacerts and SCEP GetCACert and GetNextCACert one-shot and session calls take a WolfCertEncoding; the _enc names and PEM-only forms are removed. An unknown value returns WOLFCERT_ERR_BAD_ARG. - est_call_check() holds the EST argument check, run before the dial and when a session call starts a request. Both sessions keep the encoding of the request in flight and refuse a resume with another. - est.h, scep.h, AGENTS.md, README.md, ARCHITECTURE.md and MIGRATING-FROM-WOLFSCEP.md describe blocking and _nb calls on one session type. - test_est and test_scep_async_roundtrip drop cases another test covers. --- AGENTS.md | 7 +- README.md | 12 +- cli/wolfcert_client.c | 14 +- docs/ARCHITECTURE.md | 130 ++++++++++++------ docs/MIGRATING-FROM-WOLFSCEP.md | 10 +- examples/enroll_scep.c | 3 +- src/client.c | 4 +- src/est/est_client.c | 86 ++++++------ src/internal.h | 5 +- src/scep/scep_client.c | 61 ++++---- src/scep/scep_msg.c | 12 +- tests/integration/est_client_cases.h | 3 +- tests/integration/test_est_async_roundtrip.c | 6 +- tests/integration/test_est_mldsa_roundtrip.c | 3 +- tests/integration/test_est_mtls_roundtrip.c | 7 +- .../integration/test_est_pending_roundtrip.c | 34 +++-- tests/integration/test_est_pha_roundtrip.c | 6 +- tests/integration/test_est_roundtrip.c | 7 +- tests/integration/test_est_tls_roundtrip.c | 9 +- tests/integration/test_scep_async_roundtrip.c | 49 ++++--- tests/integration/test_scep_get_cert.c | 6 +- tests/integration/test_scep_poll_roundtrip.c | 36 +++-- tests/integration/test_scep_roundtrip.c | 29 ++-- tests/unit/test_est.c | 105 +++++++++----- tests/unit/test_scep_msg.c | 44 +++++- wolfcert/est.h | 49 +++---- wolfcert/scep.h | 92 +++++++------ zephyr/tests/wolfcert_est/src/main.c | 2 +- 28 files changed, 510 insertions(+), 321 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index fe0ace6..db1f622 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -185,7 +185,7 @@ wolfcert_cleanup(); | `wolfcert/wolfcert.h` | Umbrella header, `wolfcert_init` / `wolfcert_cleanup` | | `wolfcert/client.h` | High-level `wolfcert_client_*` API | | `wolfcert/est.h`, `wolfcert/scep.h` | Protocol one-shots, sessions, non-blocking `_nb` sessions | -| `wolfcert/types.h` | `WolfCertServerCfg`, `WolfCertKeyCfg`, `WolfCertCertMeta`, `WolfCertTransport`, `wolfcert_buffer_free` | +| `wolfcert/types.h` | `WolfCertServerCfg`, `WolfCertKeyCfg`, `WolfCertCertMeta`, `WolfCertEncoding` (PEM or DER output), `WolfCertTransport`, `wolfcert_buffer_free` | | `wolfcert/keygen.h`, `wolfcert/csr.h` | Key generation, import / export, CSR building | | `wolfcert/store.h` | `WolfCertStoreOps` storage vtable, POSIX and in-memory backends | | `wolfcert/errors.h`, `wolfcert/status.h`, `wolfcert/log.h` | Error codes, last-error detail, log callback | @@ -206,7 +206,7 @@ contract, alone or in a group comment. | Network stack without BSD sockets (lwIP, NetX, FreeRTOS+TCP, wolfIP) | Fill `WolfCertServerCfg.transport` (`WolfCertTransport`); TLS records go through it too | `wolfcert/types.h`, `docs/EMBEDDED.md` section 7 | | Persistence in flash / NVM | Implement `WolfCertStoreOps` (`read` / `write` / `remove`) | `wolfcert/store.h` | | Keys in a TPM, HSM, PKCS#11 token or secure element | Register a backend with `wc_CryptoCb_RegisterDevice`, pass its id as `WolfCertKeyCfg.dev_id` | `docs/ARCHITECTURE.md` section 4.2, `examples/enroll_cryptocb.c` | -| Event loop / no blocking | `wolfcert_est_session_open_async` / `wolfcert_scep_session_open_async`, then the `_nb` calls until they stop returning `WOLFCERT_ERR_WANT_READ` / `_WANT_WRITE`; `*_session_fd` gives the descriptor to poll | `wolfcert/est.h`, `wolfcert/scep.h` | +| Event loop / no blocking | `wolfcert_est_session_open` / `wolfcert_scep_session_open`, then the `_nb` calls until they stop returning `WOLFCERT_ERR_WANT_READ` / `_WANT_WRITE`; `*_session_fd` gives the descriptor to poll | `wolfcert/est.h`, `wolfcert/scep.h` | | Logging | `wolfcert_set_log_cb` (e.g. to a UART) | `wolfcert/log.h` | | RAM budget | `max_response_bytes`, HTTP stack buffers, wolfSSL `Cert` / `CertName` sizing | `docs/EMBEDDED.md` | @@ -249,7 +249,8 @@ contract, alone or in a group comment. - **The HTTPS transport needs TLS 1.2 or newer**, or TLS 1.3 when wolfSSL is built with `WOLFSSL_NO_TLS12`. A server limited to older versions fails the handshake. -- **Non-blocking mode still blocks for DNS and the initial TCP connect.** +- **Session open still blocks for DNS and the initial TCP connect**, even + when every later call is `_nb`. - **The test server is for development and interop only**, not a production CA. - **SCEP over plain HTTP is downgradable:** an attacker who strips `AES` from diff --git a/README.md b/README.md index 57e2527..1ba93fe 100644 --- a/README.md +++ b/README.md @@ -213,12 +213,12 @@ All `WolfCertBuffer` outputs remember which heap they came from, so the same - **SCEP operations:** `GetCACaps`, `GetCACert`, `GetNextCACert`, `PKCSReq`, `RenewalReq`, `GetCert`, plus `pkiStatus=PENDING` + `GetCertInitial` polling. - **Transport:** HTTP/1.1 over wolfSSL TLS 1.3/1.2 (trust anchors, SNI, - mutual TLS), keep-alive sessions, TLS 1.3 post-handshake auth, and an - optional non-blocking mode for `poll`/`epoll`/`kqueue` event loops. Every - byte goes through the `WolfCertTransport` vtable, TLS records included, so - a non-BSD-sockets stack (FreeRTOS+TCP, NetX, lwIP, wolfIP) needs one small - glue file and no wolfCert change; the POSIX transport is the default - instance and can be compiled out. + mutual TLS), keep-alive sessions that take blocking or non-blocking (`_nb`) + calls for `poll`/`epoll`/`kqueue` event loops, and TLS 1.3 post-handshake + auth. Every byte goes through the `WolfCertTransport` vtable, TLS records + included, so a non-BSD-sockets stack (FreeRTOS+TCP, NetX, lwIP, wolfIP) + needs one small glue file and no wolfCert change; the POSIX transport is the + default instance and can be compiled out. - **Storage:** file-based cert/key store with atomic writes and 0600 key-file mode; pluggable via the `WolfCertStoreOps` vtable. - **Test servers + CLIs** for Linux/macOS (`wolfcert-server`, diff --git a/cli/wolfcert_client.c b/cli/wolfcert_client.c index 4dd9cf6..cbb85e5 100644 --- a/cli/wolfcert_client.c +++ b/cli/wolfcert_client.c @@ -1220,7 +1220,7 @@ static int scep_resolve_ca(const WolfCertServerCfg* srv, const CaPin* pin, const char* who, int pick_ra, ScepCaSet* cs) { WolfCertBuffer der = { 0 }; - int rc = wolfcert_scep_get_ca_cert_enc(srv, WOLFCERT_ENCODING_DER, &der); + int rc = wolfcert_scep_get_ca_cert(srv, WOLFCERT_ENCODING_DER, &der); memset(cs, 0, sizeof(*cs)); if (rc == WOLFCERT_OK) @@ -1380,13 +1380,13 @@ static int cmd_getcacerts(int argc, char** argv) if (ret == 0) { if (p == WOLFCERT_PROTO_EST) { #ifdef WOLFCERT_HAVE_EST - rc = wolfcert_est_get_cacerts(&srv, &pem); + rc = wolfcert_est_get_cacerts(&srv, WOLFCERT_ENCODING_PEM, &pem); #endif } else { #ifdef WOLFCERT_HAVE_SCEP - rc = wolfcert_scep_get_ca_cert_enc(&srv, WOLFCERT_ENCODING_DER, - &der); + rc = wolfcert_scep_get_ca_cert(&srv, WOLFCERT_ENCODING_DER, + &der); #endif } @@ -1574,7 +1574,8 @@ static int cmd_enroll(int argc, char** argv) rc = wolfcert_est_session_open(&srv, &es); if (rc == WOLFCERT_OK) { WolfCertBuffer ca_pem = { 0 }; - rc = wolfcert_est_session_get_cacerts(es, &ca_pem); + rc = wolfcert_est_session_get_cacerts(es, WOLFCERT_ENCODING_PEM, + &ca_pem); wolfcert_buffer_free(&ca_pem); if (rc == WOLFCERT_OK) rc = wolfcert_est_session_simple_enroll(es, csr.data, csr.len, @@ -2113,7 +2114,8 @@ static int cmd_getnextca(int argc, char** argv) } if (ret == 0) { - rc = wolfcert_scep_get_next_ca_cert(&srv, current, current_len, &pem); + rc = wolfcert_scep_get_next_ca_cert(&srv, WOLFCERT_ENCODING_PEM, + current, current_len, &pem); if (rc != WOLFCERT_OK) { fprintf(stderr, "getnextca: %s\n", wolfcert_strerror(rc)); if (pin.ca.len > 0 && rc == WOLFCERT_ERR_AUTH) { diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index dfed005..f70d7db 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -46,10 +46,11 @@ A few constraints shape the whole library: wolfCert threads that into every wolfSSL crypto call on the key. 4. **Pluggable storage and protocol handling via vtables** (see [section 5](#5-extension-points)). -5. **Event-loop-friendly I/O.** The keep-alive HTTP and EST session APIs - support a non-blocking mode that surfaces `WOLFCERT_ERR_WANT_READ` / - `WOLFCERT_ERR_WANT_WRITE` so callers can drive the flow from their own - `poll`/`epoll`/`kqueue` loop. Blocking transports are the default. +5. **Event-loop-friendly I/O.** Every keep-alive session (HTTP, EST, SCEP) + takes non-blocking `_nb` calls that surface `WOLFCERT_ERR_WANT_READ` / + `WOLFCERT_ERR_WANT_WRITE`, so callers can drive the flow from their own + `poll`/`epoll`/`kqueue` loop, and blocking calls on the same session. + Blocking transports are the default. 6. **Compile-time feature gating.** EST, SCEP, the test server, the optional key types, and a handful of wolfSSL-tuning opt-ins are all togglable; disabled subsystems disappear from the link entirely. @@ -137,15 +138,17 @@ four endpoints a typical device needs: | Endpoint | Entry point | Notes | |---|---|---| -| `GET /cacerts` | `wolfcert_est_get_cacerts` | CA chain as degenerate PKCS#7; decoded to PEM for you. | +| `GET /cacerts` | `wolfcert_est_get_cacerts` | CA chain as degenerate PKCS#7; decoded to PEM or DER for you. | | `GET /csrattrs` | `wolfcert_est_get_csr_attrs` | Raw body (empty on HTTP 204, 404, 405, 501 or an empty 200); decode with `wolfcert_est_parse_csr_attrs`. | | `POST /simpleenroll` | `wolfcert_est_simple_enroll` | Body is base64-wrapped CSR DER; 200 returns the issued cert as PKCS#7. | | `POST /simplereenroll` | `wolfcert_est_simple_reenroll` | Same, with the cert being renewed used as the implicit client identity. The test server's requirements for it are at `WolfCertServerCfgSrv.tls_client_ca_pem` in `wolfcert/server.h`. | A typical flow is: `wolfcert_key_generate` → `wolfcert_csr_build` → -`wolfcert_est_simple_enroll` → persist the issued PEM. The keep-alive -`WolfCertEstSession` carries several requests on one TLS connection, and is -what enables the post-handshake-auth bootstrap below. +`wolfcert_est_simple_enroll` → persist the issued PEM. Every endpoint also has +session calls (`wolfcert_est_session_get_cacerts`/`_nb`, etc.) on the keep-alive +`WolfCertEstSession`, which carries several requests on one TLS connection. +`wolfcert_est_session_simple_reenroll` renews the cert the session was opened +with as its client identity. **Authentication shapes.** EST supports three, all through the same `WolfCertServerCfg`: @@ -157,12 +160,12 @@ what enables the post-handshake-auth bootstrap below. 2. **mTLS up front** — set `client_cert` / `client_key`; they're presented during the handshake. 3. **TLS 1.3 post-handshake auth** — set `client_cert` / `client_key` *and* - `proto_opts.est.allow_post_handshake_auth = 1`, and use the session API. The first request - (`/cacerts`) rides an anonymous handshake; the server triggers a - mid-session `CertificateRequest` when the client first hits a protected - endpoint, and wolfSSL answers from the pre-loaded identity with no further - caller involvement. Requires a wolfSSL built with - `WOLFSSL_POST_HANDSHAKE_AUTH`. + `proto_opts.est.allow_post_handshake_auth = 1`, for one-shots and sessions + alike. The handshake is anonymous; the server sends a `CertificateRequest` + when the client first hits a protected endpoint, and wolfSSL answers from + the pre-loaded identity with no further caller involvement. On a session, + earlier requests such as `/cacerts` stay anonymous. Requires a wolfSSL + built with `WOLFSSL_POST_HANDSHAKE_AUTH`. **Test server PHA mode.** `WolfCertServerCfgSrv.tls_post_handshake_auth` turns the in-tree server into the other end of shape 3. It needs @@ -247,16 +250,14 @@ 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`/`_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. +**Keep-alive sessions.** Every SCEP operation also has session calls +(`wolfcert_scep_session_pkcs_req`/`_nb`, etc.) that reuse one connection; each +one-shot opens such a session for its one request. Open a session with +`wolfcert_scep_session_open`; each call on it is blocking, or `_nb` for an +event loop (see the session section below). Each round trip is prepared +(envelope + sign), sent, and its CertRep parsed as one logical step; an `_nb` +call pumps only the HTTP transport through `WANT_READ`/`WANT_WRITE` while the +crypto stays synchronous. **Client options** (`WolfCertServerCfg.proto_opts.scep`, a `WolfCertScepServerOpts`; all zero-init to the default behavior): - `ca_id` — CA identifier sent as `message=` on GetCACaps / GetCACert @@ -346,20 +347,63 @@ keys typically can't be exported in PEM, so `wolfcert_key_to_pem` will fail; persist the key by its `key_label` instead and resolve it against your backend on the next boot. See `examples/enroll_cryptocb.c` for a full example. -### 4.3 Non-blocking I/O in a caller-owned event loop +### 4.3 Keep-alive sessions and non-blocking I/O -The session API flips to non-blocking mode with a single flag: +A session keeps one connection for several requests. Open one when a flow +makes more than one request to the same server: SCEP's GetCACert, GetCACaps +and PKCSReq, or EST's `/cacerts` then `/simpleenroll`, which TLS 1.3 +post-handshake auth needs on one connection. Each one-shot `wolfcert_est_X` / +`wolfcert_scep_X` has a session call `wolfcert_est_session_X` / +`wolfcert_scep_session_X` that takes the session in place of `srv`: + +```c +WolfCertServerCfg srv = { .protocol = WOLFCERT_PROTO_SCEP, + .server_url = "http://ca.example/scep" }; +WolfCertScepSession* s = NULL; +WolfCertBuffer ca = { 0 }; +WolfCertScepCaps caps = { 0 }; +WolfCertScepResult res = { 0 }; + +int rc = wolfcert_scep_session_open(&srv, &s); +if (rc == WOLFCERT_OK) /* a single-cert CA: its DER */ + rc = wolfcert_scep_session_get_ca_cert(s, WOLFCERT_ENCODING_DER, &ca); +if (rc == WOLFCERT_OK) /* pin it: GetCACert is unauthenticated */ + rc = wolfcert_scep_verify_ca_fingerprint(ca.data, ca.len, fp, sizeof(fp), + WOLFCERT_SCEP_FP_SHA256); +if (rc == WOLFCERT_OK) + rc = wolfcert_scep_session_get_ca_caps(s, &caps); +if (rc == WOLFCERT_OK) + rc = wolfcert_scep_session_pkcs_req(s, &caps, ca.data, ca.len, + ca.data, ca.len, key, + csr.data, csr.len, &res); +/* On WOLFCERT_OK, res.status is SUCCESS, PENDING or FAILURE. */ + +wolfcert_scep_result_free(&res); +wolfcert_buffer_free(&ca); +wolfcert_scep_session_close(s); +``` + +A call refused with `WOLFCERT_ERR_BAD_ARG` changes nothing. A request that +fails at the protocol level (an HTTP error status, a FAILURE CertRep) leaves +the session open, though the server may close the connection after such a +reply. A failed exchange (I/O, TLS or a malformed reply), or a reply that ends +by closing the connection, closes the session: later calls return +`WOLFCERT_ERR_CONN_CLOSED`. After an I/O or connection error, open a new +session. + +Every session call also has an `_nb` form that returns instead of waiting, for +a caller-owned event loop: ```c WolfCertServerCfg srv = { /* ... as normal ... */ }; WolfCertEstSession* s; -wolfcert_est_session_open_async(&srv, &s); /* NON-BLOCKING session */ +wolfcert_est_session_open(&srv, &s); int fd = wolfcert_est_session_fd(s); WolfCertBuffer ca; for (;;) { - int rc = wolfcert_est_session_get_cacerts_nb(s, &ca); + int rc = wolfcert_est_session_get_cacerts_nb(s, WOLFCERT_ENCODING_PEM, &ca); if (rc == WOLFCERT_OK) break; if (rc == WOLFCERT_ERR_WANT_READ) { poll_fd_readable(fd); continue; } if (rc == WOLFCERT_ERR_WANT_WRITE) { poll_fd_writable(fd); continue; } @@ -370,14 +414,16 @@ for (;;) { Key points: -- **Only the session API is non-blocking.** One-shot `wolfcert_http_request` / - `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. +- **One-shots block; a session takes either style.** One-shot + `wolfcert_http_request` / `wolfcert_est_*` / `wolfcert_scep_*` calls are + blocking by design. A session takes blocking and `_nb` calls, and the style + may change between requests; a blocking call while an `_nb` request is in + flight returns `WOLFCERT_ERR_BAD_ARG`. The SCEP session additionally does + not require TLS, since SCEP authenticates at the pkiMessage layer. +- **DNS and the initial TCP connect stay synchronous.** Session open resolves + and connects; the TLS handshake runs on the first request, so it is + non-blocking under `_nb`. If you can't afford a blocking connect, resolve and + connect the socket yourself. - **The descriptor comes from the built-in transport.** `*_session_fd()` returns `-1` on a caller-supplied `WolfCertTransport`, which has no descriptor to hand out; drive the loop from whatever readiness signal your @@ -490,11 +536,11 @@ The contract: unbounded wait around a non-blocking transfer: the second shape silently discards whatever the application configured. - **The mode can change between requests on one connection.** wolfCert lets - an application make blocking and `_nb` requests on one HTTP or EST session, - one request at a time, so a transport can see `0` and a negative value on - the same connection. Take special care to honour each call's value if your - application needs this; otherwise keep a connection in one mode. Closing a - session always writes the TLS `close_notify` with `0`. + an application make blocking and `_nb` requests on one HTTP, EST or SCEP + session, one request at a time, so a transport can see `0` and a negative + value on the same connection. Take special care to honour each call's value + if your application needs this; otherwise keep a connection in one mode. + Closing a session always writes the TLS `close_notify` with `0`. - **Never write more than `len` bytes.** wolfCert rejects a count larger than it asked for, but that only keeps the overrun out of its own buffers - the write into yours has already happened, so honouring `len` is the transport's @@ -563,7 +609,7 @@ WolfCertServerCfg srv = { .proto_opts.est = { .allow_post_handshake_auth = 1 }, }; WolfCertEstSession* s; -wolfcert_est_session_open_async(&srv, &s); +wolfcert_est_session_open(&srv, &s); run_nb_loop(s); /* pattern from section 4.3 */ wolfcert_est_session_close(s); diff --git a/docs/MIGRATING-FROM-WOLFSCEP.md b/docs/MIGRATING-FROM-WOLFSCEP.md index 0551a0d..09c96c9 100644 --- a/docs/MIGRATING-FROM-WOLFSCEP.md +++ b/docs/MIGRATING-FROM-WOLFSCEP.md @@ -37,7 +37,7 @@ If you must keep your own transport, for example on an RTOS with a proprietary s | `wolfSCEP_set_getCaId` | `srv.proto_opts.scep.ca_id` | | `wolfSCEP_set_bundle` (the pkiMessage your application built) | nothing to pass: wolfCert builds the pkiMessage. You supply `csr_der`, `ra_cert` and `ca_bundle` and it does the rest | | `wolfSCEP_request(WS_REQUEST_CACAPS)` then `wolfSCEP_response` | `wolfcert_scep_get_ca_caps` | -| `wolfSCEP_request(WS_REQUEST_CA)` then `wolfSCEP_response` | `wolfcert_scep_get_ca_cert` or `wolfcert_scep_get_ca_cert_enc` | +| `wolfSCEP_request(WS_REQUEST_CA)` then `wolfSCEP_response` | `wolfcert_scep_get_ca_cert` | | `wolfSCEP_request(WS_REQUEST_ENROLL)` then `wolfSCEP_response` | `wolfcert_scep_pkcs_req` | | `WS_POST_FLAG` | automatic: POST, or the RFC 8894 section 4.1 GET form, chosen from `GetCACaps` (see [`ARCHITECTURE.md`](ARCHITECTURE.md)) | | `wolfSCEP_reply_status` (the HTTP status code) | checked internally; a non-200 surfaces as `WOLFCERT_ERR_HTTP` | @@ -45,7 +45,7 @@ If you must keep your own transport, for example on an RTOS with a proprietary s | `wolfSCEP_reply_error`, `wolfSCEP_get_error` | `wolfcert_strerror` and `wolfcert_last_error_message` | | `wolfSCEP_Debugging_ON` / `_OFF` | `wolfcert_set_log_cb` and `wolfcert_set_log_level` | | your own CA fingerprint check | `wolfcert_scep_verify_ca_fingerprint`, or `wolfcert-client --ca-fingerprint` from the CLI | -| no equivalent | `wolfcert_scep_renewal_req` (either messageType, see below), `wolfcert_scep_get_cert_initial`, `wolfcert_scep_get_cert`, `wolfcert_scep_get_next_ca_cert`, the keep-alive and async session API, and all of EST | +| no equivalent | `wolfcert_scep_renewal_req` (either messageType, see below), `wolfcert_scep_get_cert_initial`, `wolfcert_scep_get_cert`, `wolfcert_scep_get_next_ca_cert`, the keep-alive session API with blocking and `_nb` calls, and all of EST | `WS_REQUEST_CERT` and `WS_REQUEST_CRL` need no entry. Both fall through to the default case of `wolfSCEP_request()` and return `WS_BAD_ARGUMENT`, so no working integration can be using them. wolfCert covers the first anyway with `wolfcert_scep_get_cert`; it has no GetCRL, and RFC 8894 section 2.7 would rather you used the CRL distribution point. @@ -86,7 +86,7 @@ WolfCertServerCfg srv = { /* 1) GetCACert, then pin it against a fingerprint you hold out of band. */ WolfCertBuffer ca_der = { 0 }; -wolfcert_scep_get_ca_cert_enc(&srv, WOLFCERT_ENCODING_DER, &ca_der); +wolfcert_scep_get_ca_cert(&srv, WOLFCERT_ENCODING_DER, &ca_der); if (wolfcert_scep_verify_ca_fingerprint(ca_der.data, ca_der.len, expected_fp, sizeof(expected_fp), WOLFCERT_SCEP_FP_SHA256) != WOLFCERT_OK) @@ -152,7 +152,7 @@ srv.proto_opts.scep.content_cipher = WOLFCERT_SCEP_CIPHER_AES256; **Fingerprint verification is now the library's job.** Delete your helper and call `wolfcert_scep_verify_ca_fingerprint`, which hashes the DER certificate and compares in constant time. -**Error codes are a different, smaller set.** wolfSCEP's `WS_*` codes map onto `wolfcert/errors.h`: `WS_BAD_ARGUMENT` to `WOLFCERT_ERR_BAD_ARG`, `WS_MEMORY_E` to `WOLFCERT_ERR_MEMORY`, `WS_PARSE_E` to `WOLFCERT_ERR_PARSE`, the `WS_CBIO_ERR_*` family to `WOLFCERT_ERR_IO` and, in non-blocking mode only, to `WOLFCERT_ERR_WANT_READ` and `WOLFCERT_ERR_WANT_WRITE`. There is no per-call error string; use `wolfcert_strerror` for the code and `wolfcert_last_error_message` for the detail. +**Error codes are a different, smaller set.** wolfSCEP's `WS_*` codes map onto `wolfcert/errors.h`: `WS_BAD_ARGUMENT` to `WOLFCERT_ERR_BAD_ARG`, `WS_MEMORY_E` to `WOLFCERT_ERR_MEMORY`, `WS_PARSE_E` to `WOLFCERT_ERR_PARSE`, the `WS_CBIO_ERR_*` family to `WOLFCERT_ERR_IO` and, from the `_nb` calls only, to `WOLFCERT_ERR_WANT_READ` and `WOLFCERT_ERR_WANT_WRITE`. There is no per-call error string; use `wolfcert_strerror` for the code and `wolfcert_last_error_message` for the detail. **TLS is optional for SCEP and mandatory for EST.** SCEP authenticates inside the pkiMessage, so a plaintext `http://` endpoint is accepted. An `https://` endpoint requires `srv.verify_server`: every SCEP entry point, one-shot and session alike, refuses to run unverified rather than completing a silent anonymous handshake, because `verify_server` is the only peer-verification switch in the transport. @@ -189,7 +189,7 @@ Link against `libwolfcert` and include ``, which pulls in t ## What you get on top - **EST (RFC 7030)** for the same lifecycle over TLS, with ECC, Ed25519, Ed448 and ML-DSA keys, none of which SCEP permits. -- **Keep-alive and non-blocking sessions**, so several operations share one connection and an event loop can drive them through `WOLFCERT_ERR_WANT_READ` and `WOLFCERT_ERR_WANT_WRITE`. +- **Keep-alive sessions with `_nb` calls**, so several operations share one connection and an event loop can drive them through `WOLFCERT_ERR_WANT_READ` and `WOLFCERT_ERR_WANT_WRITE`. - **CryptoCb offload.** Register your TPM, HSM or PKCS#11 backend with wolfSSL and pass the resulting `dev_id` in `WolfCertKeyCfg`; the private key never has to be in memory. - **Pluggable storage** through `WolfCertStoreOps` for flash and NVM targets. - **Static memory.** Every allocation carries a heap hint, so wolfCert runs on a wolfSSL built `WOLFSSL_NO_MALLOC` with a static pool. diff --git a/examples/enroll_scep.c b/examples/enroll_scep.c index 96891f7..2f7f3a4 100644 --- a/examples/enroll_scep.c +++ b/examples/enroll_scep.c @@ -84,7 +84,8 @@ int main(int argc, char** argv) /* 1) GetCACert, converted to DER for the SCEP envelope. */ WolfCertBuffer ca_pem = { 0 }; - if (wolfcert_scep_get_ca_cert(&srv, &ca_pem) != WOLFCERT_OK) { + if (wolfcert_scep_get_ca_cert(&srv, WOLFCERT_ENCODING_PEM, + &ca_pem) != WOLFCERT_OK) { fprintf(stderr, "GetCACert failed\n"); return 2; } diff --git a/src/client.c b/src/client.c index 771885a..b8441c6 100644 --- a/src/client.c +++ b/src/client.c @@ -65,12 +65,12 @@ int wolfcert_client_get_ca(WolfCertClient* client, const WolfCertServerCfg* srv, #ifdef WOLFCERT_HAVE_EST if (srv->protocol == WOLFCERT_PROTO_EST) - return wolfcert_est_get_cacerts_enc(srv, encoding, out_ca); + return wolfcert_est_get_cacerts(srv, encoding, out_ca); #endif #ifdef WOLFCERT_HAVE_SCEP if (srv->protocol == WOLFCERT_PROTO_SCEP) - return wolfcert_scep_get_ca_cert_enc(srv, encoding, out_ca); + return wolfcert_scep_get_ca_cert(srv, encoding, out_ca); #endif return WOLFCERT_ERR_UNSUPPORTED; diff --git a/src/est/est_client.c b/src/est/est_client.c index 477572c..d8e3eaa 100644 --- a/src/est/est_client.c +++ b/src/est/est_client.c @@ -154,6 +154,7 @@ struct WolfCertEstSession { WolfCertHttpResponse in_resp; const void* in_out; /* caller's output, checked on resume */ enum est_session_op in_op; + WolfCertEncoding in_enc; }; int wolfcert_est_session_open(const WolfCertServerCfg* srv, @@ -261,7 +262,8 @@ static void est_session_reset(WolfCertEstSession* s) /* Refuse a resumed _nb call that does not match the request in flight. */ static int est_session_resume_check(const WolfCertEstSession* s, - enum est_session_op op, const void* out) + enum est_session_op op, + WolfCertEncoding enc, const void* out) { if (s->in_op != op) return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "est", @@ -270,6 +272,9 @@ static int est_session_resume_check(const WolfCertEstSession* s, return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "est", "output pointer differs from the in-flight request; pass the " "same pointer to each poll call"); + if (enc != s->in_enc) + return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "est", + "encoding differs from the in-flight request"); return WOLFCERT_OK; } @@ -306,10 +311,22 @@ static void est_call_clear(const WolfCertEstSession* s, memset(out, 0, sizeof(*out)); } +static int est_call_check(enum est_session_op op, + const uint8_t* csr_der, size_t csr_der_len, + WolfCertEncoding enc, const WolfCertBuffer* out) +{ + if (out == NULL || + (enc != WOLFCERT_ENCODING_PEM && enc != WOLFCERT_ENCODING_DER) || + (est_is_enroll(op) && (csr_der == NULL || csr_der_len == 0))) + return WOLFCERT_ERR_BAD_ARG; + + return WOLFCERT_OK; +} + /* Build the request for op into s->in_* and mark it in flight. */ static int est_session_begin(WolfCertEstSession* s, enum est_session_op op, const uint8_t* csr_der, size_t csr_der_len, - const void* out) + WolfCertEncoding enc, const void* out) { int rc = WOLFCERT_OK; @@ -339,6 +356,7 @@ static int est_session_begin(WolfCertEstSession* s, enum est_session_op op, s->in_req.body_len = s->in_body.len; } s->in_op = op; + s->in_enc = enc; s->in_out = out; s->in_active = 1; } @@ -360,8 +378,7 @@ static int est_session_call(WolfCertEstSession* s, enum est_session_op op, int rc; est_call_clear(s, out, res); - if (s == NULL || out == NULL || - (est_is_enroll(op) && (csr_der == NULL || csr_der_len == 0))) + if (s == NULL) return WOLFCERT_ERR_BAD_ARG; if (s->in_active && !nb) @@ -369,13 +386,18 @@ static int est_session_call(WolfCertEstSession* s, enum est_session_op op, "an EST request is in flight on this session; continue it with " "the _nb call"); - if (s->in_active) - rc = est_session_resume_check(s, op, out); - else if (op == EST_OP_SIMPLE_REENROLL && !s->has_client_identity) - rc = WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "est", - "reenroll needs a session opened with the cert being renewed"); - else - rc = est_session_begin(s, op, csr_der, csr_der_len, out); + if (s->in_active) { + rc = est_session_resume_check(s, op, enc, out); + } + else { + rc = est_call_check(op, csr_der, csr_der_len, enc, out); + if (rc == WOLFCERT_OK && op == EST_OP_SIMPLE_REENROLL && + !s->has_client_identity) + rc = WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "est", + "reenroll needs a session opened with the cert being renewed"); + if (rc == WOLFCERT_OK) + rc = est_session_begin(s, op, csr_der, csr_der_len, enc, out); + } if (rc != WOLFCERT_OK) return rc; @@ -399,30 +421,16 @@ static int est_session_call(WolfCertEstSession* s, enum est_session_op op, } int wolfcert_est_session_get_cacerts(WolfCertEstSession* s, - WolfCertBuffer* out_ca_pem) -{ - return est_session_call(s, EST_OP_GET_CACERTS, NULL, 0, - WOLFCERT_ENCODING_PEM, out_ca_pem, NULL, 0); -} - -int wolfcert_est_session_get_cacerts_nb(WolfCertEstSession* s, - WolfCertBuffer* out_ca_pem) -{ - return est_session_call(s, EST_OP_GET_CACERTS, NULL, 0, - WOLFCERT_ENCODING_PEM, out_ca_pem, NULL, 1); -} - -int wolfcert_est_session_get_cacerts_enc(WolfCertEstSession* s, - WolfCertEncoding enc, - WolfCertBuffer* out_ca) + WolfCertEncoding enc, + WolfCertBuffer* out_ca) { return est_session_call(s, EST_OP_GET_CACERTS, NULL, 0, enc, out_ca, NULL, 0); } -int wolfcert_est_session_get_cacerts_enc_nb(WolfCertEstSession* s, - WolfCertEncoding enc, - WolfCertBuffer* out_ca) +int wolfcert_est_session_get_cacerts_nb(WolfCertEstSession* s, + WolfCertEncoding enc, + WolfCertBuffer* out_ca) { return est_session_call(s, EST_OP_GET_CACERTS, NULL, 0, enc, out_ca, NULL, 1); @@ -499,11 +507,9 @@ static int est_one_shot(const WolfCertServerCfg* srv, enum est_session_op op, int rc; est_call_clear(NULL, out, res); - if (out == NULL || - (est_is_enroll(op) && (csr_der == NULL || csr_der_len == 0))) - return WOLFCERT_ERR_BAD_ARG; - - rc = wolfcert_est_session_open(srv, &s); + rc = est_call_check(op, csr_der, csr_der_len, enc, out); + if (rc == WOLFCERT_OK) + rc = wolfcert_est_session_open(srv, &s); if (rc == WOLFCERT_OK) { wolfcert_http_session_set_close(s->http); rc = est_session_call(s, op, csr_der, csr_der_len, enc, out, res, 0); @@ -513,14 +519,8 @@ static int est_one_shot(const WolfCertServerCfg* srv, enum est_session_op op, return rc; } -int wolfcert_est_get_cacerts(const WolfCertServerCfg* srv, WolfCertBuffer* out_ca_pem) -{ - return est_one_shot(srv, EST_OP_GET_CACERTS, NULL, 0, - WOLFCERT_ENCODING_PEM, out_ca_pem, NULL); -} - -int wolfcert_est_get_cacerts_enc(const WolfCertServerCfg* srv, WolfCertEncoding enc, - WolfCertBuffer* out_ca) +int wolfcert_est_get_cacerts(const WolfCertServerCfg* srv, WolfCertEncoding enc, + WolfCertBuffer* out_ca) { return est_one_shot(srv, EST_OP_GET_CACERTS, NULL, 0, enc, out_ca, NULL); } diff --git a/src/internal.h b/src/internal.h index 9911c1d..65d9f74 100644 --- a/src/internal.h +++ b/src/internal.h @@ -484,10 +484,11 @@ WOLFCERT_TEST_VIS int wolfcert_scep_check_cert_rep(const char* msg_type, const uint8_t* sent_tid, size_t sent_tid_len); /* Verify a GetNextCACert response is signed by a cert in current_ca_der and - * return the rollover certificates as PEM. */ + * return the rollover certificates as PEM, or as concatenated DER for + * WOLFCERT_ENCODING_DER. */ WOLFCERT_TEST_VIS int wolfcert_scep_verify_next_ca_response( const uint8_t* resp_der, size_t resp_len, const uint8_t* current_ca_der, size_t current_ca_len, - WolfCertBuffer* out_pem, void* heap); + WolfCertEncoding enc, WolfCertBuffer* out, void* heap); #endif /* WOLFCERT_INTERNAL_H */ diff --git a/src/scep/scep_client.c b/src/scep/scep_client.c index 76b39f9..62e42b6 100644 --- a/src/scep/scep_client.c +++ b/src/scep/scep_client.c @@ -647,11 +647,15 @@ static int scep_call_check(const ScepCall* c) ok = c->caps_out != NULL; break; case SCEP_OP_GET_CA_CERT: - ok = c->buf_out != NULL; + ok = c->buf_out != NULL && + (c->enc == WOLFCERT_ENCODING_PEM || + c->enc == WOLFCERT_ENCODING_DER); break; case SCEP_OP_GET_NEXT_CA_CERT: ok = c->buf_out != NULL && c->ca_bundle != NULL && - c->ca_bundle_len > 0; + c->ca_bundle_len > 0 && + (c->enc == WOLFCERT_ENCODING_PEM || + c->enc == WOLFCERT_ENCODING_DER); break; case SCEP_OP_PKCS_REQ: ok = c->csr != NULL && c->csr_len > 0; @@ -702,6 +706,7 @@ struct WolfCertScepSession { /* In-flight state of the single active round trip. */ int in_active; enum scep_session_op in_op; /* valid when in_active */ + WolfCertEncoding in_enc; char* in_url; /* owned request URL */ WolfCertBuffer in_pki; /* built pkiMessage, owned (POST body) */ WolfCertHttpRequest in_req; @@ -905,11 +910,13 @@ static void scep_session_start(WolfCertScepSession* s, const ScepCall* c, s->in_req.body_len = s->in_pki.len; } s->in_op = c->op; + s->in_enc = c->enc; s->in_out = scep_call_out(c); s->in_active = 1; } -/* A resumed _nb poll must name the in-flight operation and output. */ +/* A resumed _nb poll must name the in-flight operation, output and + * encoding. */ static int scep_session_resume_check(const WolfCertScepSession* s, const ScepCall* c) { @@ -920,6 +927,9 @@ static int scep_session_resume_check(const WolfCertScepSession* s, return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", "output pointer differs from the in-flight request; pass the " "same pointer to each poll call"); + if (c->enc != s->in_enc) + return WOLFCERT_ERR(WOLFCERT_ERR_BAD_ARG, "scep", + "encoding differs from the in-flight request"); return WOLFCERT_OK; } @@ -1301,7 +1311,8 @@ static int scep_session_finish(WolfCertScepSession* s, const ScepCall* c) /* RFC 8894 section 4.7.1: the current CA signs a SignedData whose * content is a certs-only bundle holding the next CA cert. */ rc = wolfcert_scep_verify_next_ca_response(r->body, r->body_len, - s->in_ca_bundle, s->in_ca_bundle_len, c->buf_out, s->heap); + s->in_ca_bundle, s->in_ca_bundle_len, c->enc, c->buf_out, + s->heap); } else { rc = scep_finish(s, c->res_out); @@ -1392,18 +1403,18 @@ int wolfcert_scep_session_get_ca_caps_nb(WolfCertScepSession* s, return scep_session_call(s, &c, 1); } -int wolfcert_scep_session_get_ca_cert_enc(WolfCertScepSession* s, - WolfCertEncoding enc, - WolfCertBuffer* out_ca) +int wolfcert_scep_session_get_ca_cert(WolfCertScepSession* s, + WolfCertEncoding enc, + WolfCertBuffer* out_ca) { ScepCall c = { .op = SCEP_OP_GET_CA_CERT, .enc = enc, .buf_out = out_ca }; return scep_session_call(s, &c, 0); } -int wolfcert_scep_session_get_ca_cert_enc_nb(WolfCertScepSession* s, - WolfCertEncoding enc, - WolfCertBuffer* out_ca) +int wolfcert_scep_session_get_ca_cert_nb(WolfCertScepSession* s, + WolfCertEncoding enc, + WolfCertBuffer* out_ca) { ScepCall c = { .op = SCEP_OP_GET_CA_CERT, .enc = enc, .buf_out = out_ca }; @@ -1411,25 +1422,27 @@ int wolfcert_scep_session_get_ca_cert_enc_nb(WolfCertScepSession* s, } int wolfcert_scep_session_get_next_ca_cert(WolfCertScepSession* s, + WolfCertEncoding enc, const uint8_t* current_ca_der, size_t current_ca_len, - WolfCertBuffer* out_next_ca_pem) + WolfCertBuffer* out_next_ca) { - ScepCall c = { .op = SCEP_OP_GET_NEXT_CA_CERT, + ScepCall c = { .op = SCEP_OP_GET_NEXT_CA_CERT, .enc = enc, .ca_bundle = current_ca_der, .ca_bundle_len = current_ca_len, - .buf_out = out_next_ca_pem }; + .buf_out = out_next_ca }; return scep_session_call(s, &c, 0); } int wolfcert_scep_session_get_next_ca_cert_nb(WolfCertScepSession* s, + WolfCertEncoding enc, const uint8_t* current_ca_der, size_t current_ca_len, - WolfCertBuffer* out_next_ca_pem) + WolfCertBuffer* out_next_ca) { - ScepCall c = { .op = SCEP_OP_GET_NEXT_CA_CERT, + ScepCall c = { .op = SCEP_OP_GET_NEXT_CA_CERT, .enc = enc, .ca_bundle = current_ca_der, .ca_bundle_len = current_ca_len, - .buf_out = out_next_ca_pem }; + .buf_out = out_next_ca }; return scep_session_call(s, &c, 1); } @@ -1618,13 +1631,8 @@ int wolfcert_scep_get_ca_caps(const WolfCertServerCfg* srv, WolfCertScepCaps* ou return scep_one_shot(srv, &c); } -int wolfcert_scep_get_ca_cert(const WolfCertServerCfg* srv, WolfCertBuffer* out_ca_pem) -{ - return wolfcert_scep_get_ca_cert_enc(srv, WOLFCERT_ENCODING_PEM, out_ca_pem); -} - -int wolfcert_scep_get_ca_cert_enc(const WolfCertServerCfg* srv, WolfCertEncoding enc, - WolfCertBuffer* out_ca) +int wolfcert_scep_get_ca_cert(const WolfCertServerCfg* srv, + WolfCertEncoding enc, WolfCertBuffer* out_ca) { ScepCall c = { .op = SCEP_OP_GET_CA_CERT, .enc = enc, .buf_out = out_ca }; @@ -1632,13 +1640,14 @@ int wolfcert_scep_get_ca_cert_enc(const WolfCertServerCfg* srv, WolfCertEncoding } int wolfcert_scep_get_next_ca_cert(const WolfCertServerCfg* srv, + WolfCertEncoding enc, const uint8_t* current_ca_der, size_t current_ca_len, - WolfCertBuffer* out_next_ca_pem) + WolfCertBuffer* out_next_ca) { - ScepCall c = { .op = SCEP_OP_GET_NEXT_CA_CERT, + ScepCall c = { .op = SCEP_OP_GET_NEXT_CA_CERT, .enc = enc, .ca_bundle = current_ca_der, .ca_bundle_len = current_ca_len, - .buf_out = out_next_ca_pem }; + .buf_out = out_next_ca }; return scep_one_shot(srv, &c); } diff --git a/src/scep/scep_msg.c b/src/scep/scep_msg.c index 95bd55d..f164294 100644 --- a/src/scep/scep_msg.c +++ b/src/scep/scep_msg.c @@ -649,14 +649,15 @@ WOLFCERT_TEST_VIS int wolfcert_scep_verify_next_ca_response(const uint8_t* resp_ size_t resp_len, const uint8_t* current_ca_der, size_t current_ca_len, - WolfCertBuffer* out_pem, void* heap) + WolfCertEncoding enc, + WolfCertBuffer* out, void* heap) { WolfCertBuffer content = { 0 }; uint8_t* signer = NULL; size_t signer_len = 0; int rc; - if (resp_der == NULL || current_ca_der == NULL || out_pem == NULL) + if (resp_der == NULL || current_ca_der == NULL || out == NULL) return WOLFCERT_ERR_BAD_ARG; rc = wolfcert_scep_parse_pki_message(resp_der, resp_len, &content, @@ -669,8 +670,11 @@ WOLFCERT_TEST_VIS int wolfcert_scep_verify_next_ca_response(const uint8_t* resp_ current_ca_der, current_ca_len, heap); } - if (rc == WOLFCERT_OK) { - rc = wolfcert_pkcs7_certs_to_pem(content.data, content.len, out_pem, heap); + if (rc == WOLFCERT_OK && enc == WOLFCERT_ENCODING_DER) { + rc = wolfcert_pkcs7_certs_to_der(content.data, content.len, out, heap); + } + else if (rc == WOLFCERT_OK) { + rc = wolfcert_pkcs7_certs_to_pem(content.data, content.len, out, heap); } WOLFCERT_XFREE(signer, heap); diff --git a/tests/integration/est_client_cases.h b/tests/integration/est_client_cases.h index 5faec44..ed155c2 100644 --- a/tests/integration/est_client_cases.h +++ b/tests/integration/est_client_cases.h @@ -203,7 +203,8 @@ static inline int session_basic_auth(const WolfCertServerCfg* client_cfg) } EST_CHECK(orc == WOLFCERT_OK); if (ret == 0) { - orc = wolfcert_est_session_get_cacerts(s, &ca_pem); + orc = wolfcert_est_session_get_cacerts(s, WOLFCERT_ENCODING_PEM, + &ca_pem); if (orc != WOLFCERT_OK) { fprintf(stderr, "session_get_cacerts: rc=%d (%s) wolfssl=%d %s\n", orc, wolfcert_strerror(orc), wolfcert_last_wolfssl_err(), diff --git a/tests/integration/test_est_async_roundtrip.c b/tests/integration/test_est_async_roundtrip.c index 6a70f1f..bff4f22 100644 --- a/tests/integration/test_est_async_roundtrip.c +++ b/tests/integration/test_est_async_roundtrip.c @@ -67,7 +67,8 @@ static int wait_io(WolfCertEstSession* s, int rc) static int pump_get_cacerts(WolfCertEstSession* s, WolfCertBuffer* out) { for (;;) { - int rc = wolfcert_est_session_get_cacerts_nb(s, out); + int rc = wolfcert_est_session_get_cacerts_nb(s, WOLFCERT_ENCODING_PEM, + out); if (rc == WOLFCERT_OK) return 0; if (rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE) { @@ -237,7 +238,8 @@ int main(void) wolfcert_est_result_free(&wr); /* The failed enroll no longer counts as in flight. */ - REQUIRE(wolfcert_est_session_get_cacerts_nb(bes, &bca) + REQUIRE(wolfcert_est_session_get_cacerts_nb(bes, WOLFCERT_ENCODING_PEM, + &bca) != WOLFCERT_ERR_BAD_ARG); wolfcert_buffer_free(&bca); wolfcert_est_session_close(bes); diff --git a/tests/integration/test_est_mldsa_roundtrip.c b/tests/integration/test_est_mldsa_roundtrip.c index b935967..6ded64b 100644 --- a/tests/integration/test_est_mldsa_roundtrip.c +++ b/tests/integration/test_est_mldsa_roundtrip.c @@ -138,7 +138,8 @@ int main(void) .verify_server = 1 }; WolfCertBuffer ca_pem = { 0 }; - REQUIRE(wolfcert_est_get_cacerts(&client_cfg, &ca_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_est_get_cacerts(&client_cfg, WOLFCERT_ENCODING_PEM, + &ca_pem) == WOLFCERT_OK); int rc = 0; #ifndef WOLFSSL_NO_ML_DSA_44 diff --git a/tests/integration/test_est_mtls_roundtrip.c b/tests/integration/test_est_mtls_roundtrip.c index d1927a7..9d12b02 100644 --- a/tests/integration/test_est_mtls_roundtrip.c +++ b/tests/integration/test_est_mtls_roundtrip.c @@ -99,7 +99,7 @@ static int test_reenroll_ignores_cfg_identity(const char* url, cli.client_key_len = stale_key_len; /* The server refuses the stale identity on its own */ - rc = wolfcert_est_get_cacerts(&cli, &ca_pem); + rc = wolfcert_est_get_cacerts(&cli, WOLFCERT_ENCODING_PEM, &ca_pem); wolfcert_buffer_free(&ca_pem); REQUIRE(rc != WOLFCERT_OK); @@ -604,7 +604,7 @@ int main(void) .verify_server = 1, }; WolfCertBuffer ca_pem = { 0 }; - int rc = wolfcert_est_get_cacerts(&cli, &ca_pem); + int rc = wolfcert_est_get_cacerts(&cli, WOLFCERT_ENCODING_PEM, &ca_pem); REQUIRE(rc != WOLFCERT_OK); if (rc == WOLFCERT_OK) wolfcert_buffer_free(&ca_pem); @@ -633,7 +633,8 @@ int main(void) }; WolfCertBuffer ca_pem = { 0 }; - REQUIRE(wolfcert_est_get_cacerts(&cli, &ca_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_est_get_cacerts(&cli, WOLFCERT_ENCODING_PEM, + &ca_pem) == WOLFCERT_OK); REQUIRE(ca_pem.len > 0); wolfcert_buffer_free(&ca_pem); diff --git a/tests/integration/test_est_pending_roundtrip.c b/tests/integration/test_est_pending_roundtrip.c index fd7fa59..7ac1a73 100644 --- a/tests/integration/test_est_pending_roundtrip.c +++ b/tests/integration/test_est_pending_roundtrip.c @@ -82,7 +82,8 @@ static int pump_enroll_nb(WolfCertEstSession* s, const uint8_t* csr, static int pump_cacerts_nb(WolfCertEstSession* s, WolfCertBuffer* out) { for (;;) { - int rc = wolfcert_est_session_get_cacerts_nb(s, out); + int rc = wolfcert_est_session_get_cacerts_nb(s, WOLFCERT_ENCODING_PEM, + out); if (rc != WOLFCERT_ERR_WANT_READ && rc != WOLFCERT_ERR_WANT_WRITE) return rc; if (wait_io(s, rc) != 0) @@ -140,7 +141,8 @@ static int pending_path(WolfCertServer* s) .verify_server = 1 }; WolfCertBuffer ca_pem = { 0 }; - REQUIRE(wolfcert_est_get_cacerts(&cli, &ca_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_est_get_cacerts(&cli, WOLFCERT_ENCODING_PEM, + &ca_pem) == WOLFCERT_OK); /* The one-shot reports PENDING with the Retry-After hint, then SUCCESS. */ WolfCertKey* dk_ex = NULL; @@ -275,7 +277,8 @@ static int pending_path(WolfCertServer* s) wolfcert_est_result_free(&sr3); WolfCertBuffer sess_ca = { 0 }; - REQUIRE(wolfcert_est_session_get_cacerts(xsess, &sess_ca) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_get_cacerts(xsess, WOLFCERT_ENCODING_PEM, + &sess_ca) == WOLFCERT_OK); REQUIRE(sess_ca.len > 0); wolfcert_buffer_free(&sess_ca); @@ -310,7 +313,8 @@ static int pending_path(WolfCertServer* s) /* A blocking call works on the session after its _nb requests. */ WolfCertBuffer async_ca = { 0 }; - REQUIRE(wolfcert_est_session_get_cacerts(nsess, &async_ca) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_get_cacerts(nsess, WOLFCERT_ENCODING_PEM, + &async_ca) == WOLFCERT_OK); REQUIRE(async_ca.len > 0); wolfcert_buffer_free(&async_ca); @@ -338,7 +342,8 @@ static int pending_path(WolfCertServer* s) WolfCertEstSession* msess = NULL; REQUIRE(wolfcert_est_session_open(&bh_cli, &msess) == WOLFCERT_OK); WolfCertBuffer mix_ca = { 0 }; - rc = wolfcert_est_session_get_cacerts_nb(msess, &mix_ca); + rc = wolfcert_est_session_get_cacerts_nb(msess, WOLFCERT_ENCODING_PEM, + &mix_ca); REQUIRE(rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE); WolfCertEstResult mr = { 0 }; @@ -348,7 +353,12 @@ static int pending_path(WolfCertServer* s) REQUIRE(mr.status == WOLFCERT_EST_STATUS_UNSET); REQUIRE(mr.cert_pem.data == NULL); WolfCertBuffer other_ca = { 0 }; - REQUIRE(wolfcert_est_session_get_cacerts_nb(msess, &other_ca) + REQUIRE(wolfcert_est_session_get_cacerts_nb(msess, WOLFCERT_ENCODING_PEM, + &other_ca) + == WOLFCERT_ERR_BAD_ARG); + /* So does the in-flight output with another encoding. */ + REQUIRE(wolfcert_est_session_get_cacerts_nb(msess, WOLFCERT_ENCODING_DER, + &mix_ca) == WOLFCERT_ERR_BAD_ARG); /* Blocking session requests are refused as well. */ @@ -356,20 +366,23 @@ static int pending_path(WolfCertServer* s) csr_mix.len, &mr) == WOLFCERT_ERR_BAD_ARG); REQUIRE(mr.status == WOLFCERT_EST_STATUS_UNSET); - REQUIRE(wolfcert_est_session_get_cacerts(msess, &other_ca) + REQUIRE(wolfcert_est_session_get_cacerts(msess, WOLFCERT_ENCODING_PEM, + &other_ca) == WOLFCERT_ERR_BAD_ARG); REQUIRE(other_ca.data == NULL); /* A blocking call with the in-flight output is refused too, and keeps * the request: another operation still gets BAD_ARG. */ - REQUIRE(wolfcert_est_session_get_cacerts(msess, &mix_ca) + REQUIRE(wolfcert_est_session_get_cacerts(msess, WOLFCERT_ENCODING_PEM, + &mix_ca) == WOLFCERT_ERR_BAD_ARG); REQUIRE(wolfcert_est_session_simple_enroll_nb(msess, csr_mix.data, csr_mix.len, &mr) == WOLFCERT_ERR_BAD_ARG); /* The refused calls left the /cacerts request in flight. */ - rc = wolfcert_est_session_get_cacerts_nb(msess, &mix_ca); + rc = wolfcert_est_session_get_cacerts_nb(msess, WOLFCERT_ENCODING_PEM, + &mix_ca); REQUIRE(rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE); REQUIRE(mix_ca.data == NULL); wolfcert_est_session_close(msess); @@ -395,7 +408,8 @@ static int pending_path(WolfCertServer* s) REQUIRE(mr.cert_pem.data == NULL && mr.cert_pem.len == 0); REQUIRE(mr.retry_after_sec == 0 && mr.heap == NULL); - REQUIRE(wolfcert_est_session_get_cacerts_nb(msess, &mix_ca) + REQUIRE(wolfcert_est_session_get_cacerts_nb(msess, WOLFCERT_ENCODING_PEM, + &mix_ca) == WOLFCERT_ERR_BAD_ARG); REQUIRE(mix_ca.data == NULL); WolfCertEstResult other_r = { 0 }; diff --git a/tests/integration/test_est_pha_roundtrip.c b/tests/integration/test_est_pha_roundtrip.c index 727a0b7..e55f4f6 100644 --- a/tests/integration/test_est_pha_roundtrip.c +++ b/tests/integration/test_est_pha_roundtrip.c @@ -68,7 +68,8 @@ static int expect_enroll_refused(const WolfCertServerCfg* cli, const char* cn) REQUIRE(wolfcert_est_session_open(cli, &s) == WOLFCERT_OK); WolfCertBuffer ca_pem = { 0 }; - REQUIRE(wolfcert_est_session_get_cacerts(s, &ca_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_get_cacerts(s, WOLFCERT_ENCODING_PEM, + &ca_pem) == WOLFCERT_OK); REQUIRE(ca_pem.len > 0); wolfcert_buffer_free(&ca_pem); @@ -175,7 +176,8 @@ int main(void) /* /cacerts on the anonymous leg of the TLS connection. */ WolfCertBuffer ca_pem = { 0 }; - REQUIRE(wolfcert_est_session_get_cacerts(s, &ca_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_get_cacerts(s, WOLFCERT_ENCODING_PEM, + &ca_pem) == WOLFCERT_OK); REQUIRE(ca_pem.len > 0); /* /simpleenroll triggers the server's CertificateRequest. */ diff --git a/tests/integration/test_est_roundtrip.c b/tests/integration/test_est_roundtrip.c index fc0bbc3..9cef800 100644 --- a/tests/integration/test_est_roundtrip.c +++ b/tests/integration/test_est_roundtrip.c @@ -269,7 +269,8 @@ int main(void) client_cfg.transport = counting_transport; WolfCertBuffer ca_pem = { 0 }; - REQUIRE(wolfcert_est_get_cacerts(&client_cfg, &ca_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_est_get_cacerts(&client_cfg, WOLFCERT_ENCODING_PEM, + &ca_pem) == WOLFCERT_OK); /* For the single-cert test CA the DER result is that cert's raw DER. */ WolfCertClient* client = NULL; @@ -287,8 +288,8 @@ int main(void) WolfCertEstSession* ca_sess = NULL; WolfCertBuffer sess_der = { 0 }; REQUIRE(wolfcert_est_session_open(&client_cfg, &ca_sess) == WOLFCERT_OK); - REQUIRE(wolfcert_est_session_get_cacerts_enc(ca_sess, WOLFCERT_ENCODING_DER, - &sess_der) == WOLFCERT_OK); + REQUIRE(wolfcert_est_session_get_cacerts(ca_sess, WOLFCERT_ENCODING_DER, + &sess_der) == WOLFCERT_OK); wolfcert_est_session_close(ca_sess); REQUIRE(sess_der.len == ca_der.len && memcmp(sess_der.data, ca_der.data, ca_der.len) == 0); diff --git a/tests/integration/test_est_tls_roundtrip.c b/tests/integration/test_est_tls_roundtrip.c index 624ebc1..9809257 100644 --- a/tests/integration/test_est_tls_roundtrip.c +++ b/tests/integration/test_est_tls_roundtrip.c @@ -384,7 +384,8 @@ int main(void) }; WolfCertBuffer ca_pem = { 0 }; - REQUIRE(wolfcert_est_get_cacerts(&cli, &ca_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_est_get_cacerts(&cli, WOLFCERT_ENCODING_PEM, + &ca_pem) == WOLFCERT_OK); REQUIRE(ca_pem.len > 0); WolfCertKeyCfg kcfg = { .type = TEST_ENROLL_KEY_TYPE, .param = TEST_ENROLL_KEY_PARAM, @@ -416,7 +417,8 @@ int main(void) .verify_server = 1, }; WolfCertBuffer ca_pem_der = { 0 }; - REQUIRE(wolfcert_est_get_cacerts(&cli_der, &ca_pem_der) == WOLFCERT_OK); + REQUIRE(wolfcert_est_get_cacerts(&cli_der, WOLFCERT_ENCODING_PEM, + &ca_pem_der) == WOLFCERT_OK); REQUIRE(ca_pem_der.len > 0); wolfcert_buffer_free(&ca_pem_der); wc_FreeDer(&ta_der); @@ -436,7 +438,8 @@ int main(void) .verify_server = 1, }; WolfCertBuffer ca_pem_chain = { 0 }; - REQUIRE(wolfcert_est_get_cacerts(&cli_chain, &ca_pem_chain) == WOLFCERT_OK); + REQUIRE(wolfcert_est_get_cacerts(&cli_chain, WOLFCERT_ENCODING_PEM, + &ca_pem_chain) == WOLFCERT_OK); REQUIRE(ca_pem_chain.len > 0); wolfcert_buffer_free(&ca_pem_chain); free(ta_chain); diff --git a/tests/integration/test_scep_async_roundtrip.c b/tests/integration/test_scep_async_roundtrip.c index 86231c3..a8da75d 100644 --- a/tests/integration/test_scep_async_roundtrip.c +++ b/tests/integration/test_scep_async_roundtrip.c @@ -188,7 +188,8 @@ static int bootstrap(const WolfCertServerCfg* cli, const char* cn, { if (wolfcert_scep_get_ca_caps(cli, caps) != WOLFCERT_OK) return -1; - if (wolfcert_scep_get_ca_cert(cli, ca_pem) != WOLFCERT_OK) + if (wolfcert_scep_get_ca_cert(cli, WOLFCERT_ENCODING_PEM, + ca_pem) != WOLFCERT_OK) return -1; if (wc_PemToDer(ca_pem->data, (long)ca_pem->len, CERT_TYPE, ca_der, NULL, NULL, NULL) != 0) { @@ -467,8 +468,8 @@ static int async_renewal_path(WolfCertServer* s) return ret; } -/* Scenario D: _nb on a blocking session, _ex on an async one, and a second - * call mid-request with another result pointer or operation all get BAD_ARG. */ +/* Scenario D: a call mid-request with another result pointer or operation, + * or a blocking call, gets BAD_ARG and leaves the request in flight. */ static int async_guard_path(WolfCertServer* s) { char real_url[128]; @@ -483,6 +484,7 @@ static int async_guard_path(WolfCertServer* s) WolfCertScepSession* asess = NULL; WolfCertScepResult r1 = { 0 }; WolfCertScepResult r2 = { 0 }; + WolfCertBuffer gca = { 0 }; uint16_t bh_port = 0; int bh_fd = -1; int rc = 0; @@ -555,6 +557,15 @@ static int async_guard_path(WolfCertServer* s) REQUIRE_CLEAN(rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE); REQUIRE_CLEAN(r1.fail_info == 42); + /* encoding guard: a GetCACert in flight refuses another encoding. */ + wolfcert_scep_session_close(asess); + asess = NULL; + REQUIRE_CLEAN(wolfcert_scep_session_open(&cli, &asess) == WOLFCERT_OK); + rc = wolfcert_scep_session_get_ca_cert_nb(asess, WOLFCERT_ENCODING_PEM, &gca); + REQUIRE_CLEAN(rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE); + REQUIRE_CLEAN(wolfcert_scep_session_get_ca_cert_nb(asess, + WOLFCERT_ENCODING_DER, &gca) == WOLFCERT_ERR_BAD_ARG); + ret = 0; cleanup: if (asess != NULL) @@ -563,6 +574,7 @@ static int async_guard_path(WolfCertServer* s) close(bh_fd); wolfcert_scep_result_free(&r1); wolfcert_scep_result_free(&r2); + wolfcert_buffer_free(&gca); if (ca_der != NULL) wc_FreeDer(&ca_der); wolfcert_buffer_free(&ca_pem); @@ -1081,7 +1093,7 @@ static int negative_args_path(WolfCertServer* s) } /* Scenario L: GetCACaps, GetCACert, GetNextCACert and GetCert on one session, - * blocking and then _nb, each returning what the one-shot returns. */ + * blocking and then _nb. */ static int session_get_ops_path(WolfCertServer* s) { char url[128]; @@ -1092,7 +1104,6 @@ static int session_get_ops_path(WolfCertServer* s) DerBuffer* ca_der = NULL; WolfCertKey* dk = NULL; WolfCertBuffer csr = { 0 }; - WolfCertBuffer next = { 0 }; WolfCertBuffer sca = { 0 }; WolfCertBuffer snext = { 0 }; WolfCertScepSession* sess = NULL; @@ -1109,8 +1120,6 @@ static int session_get_ops_path(WolfCertServer* s) cli = (WolfCertServerCfg){ .protocol = WOLFCERT_PROTO_SCEP, .server_url = url }; REQUIRE_CLEAN(bootstrap(&cli, "CN=session-get-ops", &caps, &ca_pem, &ca_der, &dk, &csr) == 0); - REQUIRE_CLEAN(wolfcert_scep_get_next_ca_cert(&cli, ca_der->buffer, - ca_der->length, &next) == WOLFCERT_OK); /* A cert to fetch back by its serial. */ REQUIRE_CLEAN(wolfcert_scep_pkcs_req(&cli, &caps, ca_der->buffer, @@ -1126,14 +1135,17 @@ static int session_get_ops_path(WolfCertServer* s) REQUIRE_CLEAN(wolfcert_scep_session_open(&cli, &sess) == WOLFCERT_OK); REQUIRE_CLEAN(wolfcert_scep_session_get_ca_caps(sess, &scaps) == WOLFCERT_OK); REQUIRE_CLEAN(memcmp(&scaps, &caps, sizeof(caps)) == 0); - REQUIRE_CLEAN(wolfcert_scep_session_get_ca_cert_enc(sess, + REQUIRE_CLEAN(wolfcert_scep_session_get_ca_cert(sess, WOLFCERT_ENCODING_DER, &sca) == WOLFCERT_OK); REQUIRE_CLEAN(sca.len == ca_der->length && memcmp(sca.data, ca_der->buffer, sca.len) == 0); - REQUIRE_CLEAN(wolfcert_scep_session_get_next_ca_cert(sess, ca_der->buffer, - ca_der->length, &snext) == WOLFCERT_OK); - REQUIRE_CLEAN(snext.len == next.len && - memcmp(snext.data, next.data, next.len) == 0); + REQUIRE_CLEAN(wolfcert_scep_session_get_next_ca_cert(sess, + WOLFCERT_ENCODING_PEM, ca_der->buffer, ca_der->length, + &snext) == WOLFCERT_OK); + REQUIRE_CLEAN(memmem(snext.data, snext.len, + "BEGIN CERTIFICATE", 17) != NULL); + REQUIRE_CLEAN(snext.len != ca_pem.len || + memcmp(snext.data, ca_pem.data, ca_pem.len) != 0); REQUIRE_CLEAN(wolfcert_scep_session_get_cert(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, issued_der->buffer, issued_der->length, dk, dc.serial, @@ -1154,7 +1166,7 @@ static int session_get_ops_path(WolfCertServer* s) REQUIRE_CLEAN(rc == WOLFCERT_OK); REQUIRE_CLEAN(memcmp(&scaps, &caps, sizeof(caps)) == 0); do { - rc = wolfcert_scep_session_get_ca_cert_enc_nb(sess, + rc = wolfcert_scep_session_get_ca_cert_nb(sess, WOLFCERT_ENCODING_DER, &sca); } while ((rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE) && wait_ready(fd, rc) == 0); @@ -1162,13 +1174,15 @@ static int session_get_ops_path(WolfCertServer* s) REQUIRE_CLEAN(sca.len == ca_der->length && memcmp(sca.data, ca_der->buffer, sca.len) == 0); do { - rc = wolfcert_scep_session_get_next_ca_cert_nb(sess, ca_der->buffer, - ca_der->length, &snext); + rc = wolfcert_scep_session_get_next_ca_cert_nb(sess, + WOLFCERT_ENCODING_PEM, ca_der->buffer, ca_der->length, &snext); } while ((rc == WOLFCERT_ERR_WANT_READ || rc == WOLFCERT_ERR_WANT_WRITE) && wait_ready(fd, rc) == 0); REQUIRE_CLEAN(rc == WOLFCERT_OK); - REQUIRE_CLEAN(snext.len == next.len && - memcmp(snext.data, next.data, next.len) == 0); + REQUIRE_CLEAN(memmem(snext.data, snext.len, + "BEGIN CERTIFICATE", 17) != NULL); + REQUIRE_CLEAN(snext.len != ca_pem.len || + memcmp(snext.data, ca_pem.data, ca_pem.len) != 0); do { rc = wolfcert_scep_session_get_cert_nb(sess, &caps, ca_der->buffer, ca_der->length, ca_der->buffer, ca_der->length, @@ -1193,7 +1207,6 @@ static int session_get_ops_path(WolfCertServer* s) wolfcert_scep_result_free(&issued); wolfcert_buffer_free(&snext); wolfcert_buffer_free(&sca); - wolfcert_buffer_free(&next); if (ca_der != NULL) wc_FreeDer(&ca_der); wolfcert_buffer_free(&ca_pem); diff --git a/tests/integration/test_scep_get_cert.c b/tests/integration/test_scep_get_cert.c index 906da29..88308fa 100644 --- a/tests/integration/test_scep_get_cert.c +++ b/tests/integration/test_scep_get_cert.c @@ -63,7 +63,8 @@ static int get_cert_path(WolfCertServer* s) REQUIRE(wolfcert_scep_get_ca_caps(&cli, &caps) == WOLFCERT_OK); WolfCertBuffer ca_pem = { 0 }; - REQUIRE(wolfcert_scep_get_ca_cert(&cli, &ca_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_scep_get_ca_cert(&cli, WOLFCERT_ENCODING_PEM, + &ca_pem) == WOLFCERT_OK); DerBuffer* ca_der = NULL; REQUIRE(wc_PemToDer(ca_pem.data, (long)ca_pem.len, CERT_TYPE, &ca_der, NULL, NULL, NULL) == 0); @@ -317,7 +318,8 @@ static int disabled_path(WolfCertServer* s) REQUIRE(wolfcert_scep_get_ca_caps(&cli, &caps) == WOLFCERT_OK); WolfCertBuffer ca_pem = { 0 }; - REQUIRE(wolfcert_scep_get_ca_cert(&cli, &ca_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_scep_get_ca_cert(&cli, WOLFCERT_ENCODING_PEM, + &ca_pem) == WOLFCERT_OK); DerBuffer* ca_der = NULL; REQUIRE(wc_PemToDer(ca_pem.data, (long)ca_pem.len, CERT_TYPE, &ca_der, NULL, NULL, NULL) == 0); diff --git a/tests/integration/test_scep_poll_roundtrip.c b/tests/integration/test_scep_poll_roundtrip.c index 6ad27e2..467afde 100644 --- a/tests/integration/test_scep_poll_roundtrip.c +++ b/tests/integration/test_scep_poll_roundtrip.c @@ -177,7 +177,8 @@ static int poll_path(WolfCertServer* s) REQUIRE(wolfcert_scep_get_ca_caps(&cli, &caps) == WOLFCERT_OK); WolfCertBuffer ca_pem = { 0 }; - REQUIRE(wolfcert_scep_get_ca_cert(&cli, &ca_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_scep_get_ca_cert(&cli, WOLFCERT_ENCODING_PEM, + &ca_pem) == WOLFCERT_OK); DerBuffer* ca_der = NULL; REQUIRE(wc_PemToDer(ca_pem.data, (long)ca_pem.len, CERT_TYPE, &ca_der, NULL, NULL, NULL) == 0); @@ -513,7 +514,8 @@ static int empty_subject_path(WolfCertServer* s) WolfCertScepCaps caps = { 0 }; REQUIRE(wolfcert_scep_get_ca_caps(&cli, &caps) == WOLFCERT_OK); WolfCertBuffer ca_pem = { 0 }; - REQUIRE(wolfcert_scep_get_ca_cert(&cli, &ca_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_scep_get_ca_cert(&cli, WOLFCERT_ENCODING_PEM, + &ca_pem) == WOLFCERT_OK); DerBuffer* ca_der = NULL; REQUIRE(wc_PemToDer(ca_pem.data, (long)ca_pem.len, CERT_TYPE, &ca_der, NULL, NULL, NULL) == 0); @@ -567,14 +569,16 @@ static int next_ca_path(WolfCertServer* s) REQUIRE(caps.get_next_ca_cert); WolfCertBuffer current = { 0 }; - REQUIRE(wolfcert_scep_get_ca_cert(&cli, ¤t) == WOLFCERT_OK); + REQUIRE(wolfcert_scep_get_ca_cert(&cli, WOLFCERT_ENCODING_PEM, + ¤t) == WOLFCERT_OK); DerBuffer* current_der = NULL; REQUIRE(wc_PemToDer(current.data, (long)current.len, CERT_TYPE, ¤t_der, NULL, NULL, NULL) == 0); /* Accepted when bound to the current CA, which signed the response. */ WolfCertBuffer next = { 0 }; - REQUIRE(wolfcert_scep_get_next_ca_cert(&cli, current_der->buffer, + REQUIRE(wolfcert_scep_get_next_ca_cert(&cli, WOLFCERT_ENCODING_PEM, + current_der->buffer, current_der->length, &next) == WOLFCERT_OK); REQUIRE(memmem(next.data, next.len, "BEGIN CERTIFICATE", 17) != NULL); @@ -585,7 +589,8 @@ static int next_ca_path(WolfCertServer* s) /* Second call must return the cached next CA (same bytes). */ WolfCertBuffer next2 = { 0 }; - REQUIRE(wolfcert_scep_get_next_ca_cert(&cli, current_der->buffer, + REQUIRE(wolfcert_scep_get_next_ca_cert(&cli, WOLFCERT_ENCODING_PEM, + current_der->buffer, current_der->length, &next2) == WOLFCERT_OK); REQUIRE(next.len == next2.len); @@ -596,10 +601,21 @@ static int next_ca_path(WolfCertServer* s) REQUIRE(wc_PemToDer(next.data, (long)next.len, CERT_TYPE, &rollover_der, NULL, NULL, NULL) == 0); WolfCertBuffer reject = { 0 }; - REQUIRE(wolfcert_scep_get_next_ca_cert(&cli, rollover_der->buffer, + REQUIRE(wolfcert_scep_get_next_ca_cert(&cli, WOLFCERT_ENCODING_PEM, + rollover_der->buffer, rollover_der->length, &reject) != WOLFCERT_OK); + /* WOLFCERT_ENCODING_DER returns the same roll-over cert as DER. */ + WolfCertBuffer next_der = { 0 }; + REQUIRE(wolfcert_scep_get_next_ca_cert(&cli, WOLFCERT_ENCODING_DER, + current_der->buffer, + current_der->length, &next_der) + == WOLFCERT_OK); + REQUIRE(next_der.len == rollover_der->length && + memcmp(next_der.data, rollover_der->buffer, next_der.len) == 0); + wolfcert_buffer_free(&next_der); + wc_FreeDer(&rollover_der); wc_FreeDer(¤t_der); wolfcert_buffer_free(&reject); @@ -628,14 +644,16 @@ static int next_ca_disabled_path(void) REQUIRE(caps.get_next_ca_cert == 0); WolfCertBuffer ca = { 0 }; - REQUIRE(wolfcert_scep_get_ca_cert(&cli, &ca) == WOLFCERT_OK); + REQUIRE(wolfcert_scep_get_ca_cert(&cli, WOLFCERT_ENCODING_PEM, + &ca) == WOLFCERT_OK); DerBuffer* ca_der = NULL; REQUIRE(wc_PemToDer(ca.data, (long)ca.len, CERT_TYPE, &ca_der, NULL, NULL, NULL) == 0); WolfCertBuffer next = { 0 }; - int rc = wolfcert_scep_get_next_ca_cert(&cli, ca_der->buffer, - ca_der->length, &next); + int rc = wolfcert_scep_get_next_ca_cert(&cli, WOLFCERT_ENCODING_PEM, + ca_der->buffer, ca_der->length, + &next); REQUIRE(rc == WOLFCERT_ERR_NOT_FOUND); wc_FreeDer(&ca_der); diff --git a/tests/integration/test_scep_roundtrip.c b/tests/integration/test_scep_roundtrip.c index 4341cda..9cc1386 100644 --- a/tests/integration/test_scep_roundtrip.c +++ b/tests/integration/test_scep_roundtrip.c @@ -737,7 +737,7 @@ static int fetch_ca(const char* content_type, const uint8_t* body, sizeof(url)) == 0); WolfCertServerCfg cli = { .protocol = WOLFCERT_PROTO_SCEP, .server_url = url }; - int rc = wolfcert_scep_get_ca_cert_enc(&cli, enc, out); + int rc = wolfcert_scep_get_ca_cert(&cli, enc, out); pthread_join(tid, NULL); return rc; } @@ -1217,7 +1217,8 @@ static int check_getnextca_ca_id(const uint8_t* ca_der_buf, size_t ca_der_len) }; WolfCertBuffer next = { 0 }; - (void)wolfcert_scep_get_next_ca_cert(&cli, ca_der_buf, ca_der_len, &next); + (void)wolfcert_scep_get_next_ca_cert(&cli, WOLFCERT_ENCODING_PEM, + ca_der_buf, ca_der_len, &next); wolfcert_buffer_free(&next); pthread_join(tid, NULL); @@ -1861,10 +1862,10 @@ static int test_split_ra(void) rc = wolfcert_scep_get_ca_caps(&cli, &caps); if (rc == WOLFCERT_OK) - rc = wolfcert_scep_get_ca_cert(&cli, &pem); + rc = wolfcert_scep_get_ca_cert(&cli, WOLFCERT_ENCODING_PEM, &pem); if (rc == WOLFCERT_OK) - rc = wolfcert_scep_get_ca_cert_enc(&cli, WOLFCERT_ENCODING_DER, - &bundle); + rc = wolfcert_scep_get_ca_cert(&cli, WOLFCERT_ENCODING_DER, + &bundle); if (rc == WOLFCERT_OK) { second = (const char*)memmem(pem.data + 1, pem.len - 1, "-----BEGIN CERTIFICATE", 22); @@ -1996,7 +1997,8 @@ int main(void) REQUIRE(caps.renewal); WolfCertBuffer ca_pem = { 0 }; - REQUIRE(wolfcert_scep_get_ca_cert(&cli, &ca_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_scep_get_ca_cert(&cli, WOLFCERT_ENCODING_PEM, + &ca_pem) == WOLFCERT_OK); DerBuffer* ca_der = NULL; REQUIRE(wc_PemToDer(ca_pem.data, (long)ca_pem.len, CERT_TYPE, &ca_der, NULL, NULL, NULL) == 0); @@ -2186,7 +2188,8 @@ int main(void) WolfCertScepCaps tls_caps = { 0 }; WolfCertBuffer tls_ca = { 0 }; REQUIRE(wolfcert_scep_get_ca_caps(&tls_cli, &tls_caps) == WOLFCERT_ERR_TLS); - REQUIRE(wolfcert_scep_get_ca_cert(&tls_cli, &tls_ca) == WOLFCERT_ERR_TLS); + REQUIRE(wolfcert_scep_get_ca_cert(&tls_cli, WOLFCERT_ENCODING_PEM, + &tls_ca) == WOLFCERT_ERR_TLS); wolfcert_buffer_free(&tls_ca); /* With verify_server 1, GetCACaps to the closed port 1 fails with an @@ -2268,7 +2271,8 @@ int main(void) WolfCertScepCaps caps2 = { 0 }; REQUIRE(wolfcert_scep_get_ca_caps(&cli2, &caps2) == WOLFCERT_OK); WolfCertBuffer ca2_pem = { 0 }; - REQUIRE(wolfcert_scep_get_ca_cert(&cli2, &ca2_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_scep_get_ca_cert(&cli2, WOLFCERT_ENCODING_PEM, + &ca2_pem) == WOLFCERT_OK); DerBuffer* ca2_der = NULL; REQUIRE(wc_PemToDer(ca2_pem.data, (long)ca2_pem.len, CERT_TYPE, &ca2_der, NULL, NULL, NULL) == 0); @@ -2401,7 +2405,8 @@ int main(void) WolfCertScepCaps caps3 = { 0 }; REQUIRE(wolfcert_scep_get_ca_caps(&cli3, &caps3) == WOLFCERT_OK); WolfCertBuffer ca3_pem = { 0 }; - REQUIRE(wolfcert_scep_get_ca_cert(&cli3, &ca3_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_scep_get_ca_cert(&cli3, WOLFCERT_ENCODING_PEM, + &ca3_pem) == WOLFCERT_OK); DerBuffer* ca3_der = NULL; REQUIRE(wc_PemToDer(ca3_pem.data, (long)ca3_pem.len, CERT_TYPE, &ca3_der, NULL, NULL, NULL) == 0); @@ -2443,7 +2448,8 @@ int main(void) WolfCertScepCaps caps4 = { 0 }; REQUIRE(wolfcert_scep_get_ca_caps(&cli4, &caps4) == WOLFCERT_OK); WolfCertBuffer ca4_pem = { 0 }; - REQUIRE(wolfcert_scep_get_ca_cert(&cli4, &ca4_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_scep_get_ca_cert(&cli4, WOLFCERT_ENCODING_PEM, + &ca4_pem) == WOLFCERT_OK); DerBuffer* ca4_der = NULL; REQUIRE(wc_PemToDer(ca4_pem.data, (long)ca4_pem.len, CERT_TYPE, &ca4_der, NULL, NULL, NULL) == 0); @@ -2485,7 +2491,8 @@ int main(void) WolfCertScepCaps caps5 = { 0 }; REQUIRE(wolfcert_scep_get_ca_caps(&cli5, &caps5) == WOLFCERT_OK); WolfCertBuffer ca5_pem = { 0 }; - REQUIRE(wolfcert_scep_get_ca_cert(&cli5, &ca5_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_scep_get_ca_cert(&cli5, WOLFCERT_ENCODING_PEM, + &ca5_pem) == WOLFCERT_OK); DerBuffer* ca5_der = NULL; REQUIRE(wc_PemToDer(ca5_pem.data, (long)ca5_pem.len, CERT_TYPE, &ca5_der, NULL, NULL, NULL) == 0); diff --git a/tests/unit/test_est.c b/tests/unit/test_est.c index 631781e..d5b11a8 100644 --- a/tests/unit/test_est.c +++ b/tests/unit/test_est.c @@ -295,7 +295,8 @@ static int test_est_require_server_auth(void) WolfCertKey* rk = NULL; /* Each call gets ERR_TLS, even simple_enroll with a placeholder CSR. */ - REQUIRE(wolfcert_est_get_cacerts(&srv, &out) == WOLFCERT_ERR_TLS); + REQUIRE(wolfcert_est_get_cacerts(&srv, WOLFCERT_ENCODING_PEM, + &out) == WOLFCERT_ERR_TLS); REQUIRE(wolfcert_est_get_csr_attrs(&srv, &out) == WOLFCERT_ERR_TLS); REQUIRE(wolfcert_est_simple_enroll(&srv, dummy_csr, sizeof(dummy_csr), &res) == WOLFCERT_ERR_TLS); @@ -317,22 +318,42 @@ static int test_est_require_server_auth(void) /* A pinned trust anchor alone does not count as server authentication. */ srv.trust_anchors = dummy_ta; srv.trust_anchors_len = sizeof(dummy_ta); - REQUIRE(wolfcert_est_get_cacerts(&srv, &out) == WOLFCERT_ERR_TLS); + REQUIRE(wolfcert_est_get_cacerts(&srv, WOLFCERT_ENCODING_PEM, + &out) == WOLFCERT_ERR_TLS); REQUIRE(wolfcert_est_session_open(&srv, &sess) == WOLFCERT_ERR_TLS); /* With verify_server on, the dial to port 1 fails with WOLFCERT_ERR_IO. */ srv.verify_server = 1; - REQUIRE(wolfcert_est_get_cacerts(&srv, &out) == WOLFCERT_ERR_IO); + REQUIRE(wolfcert_est_get_cacerts(&srv, WOLFCERT_ENCODING_PEM, + &out) == WOLFCERT_ERR_IO); REQUIRE(wolfcert_est_session_open(&srv, &sess) == WOLFCERT_ERR_IO); /* Bad arguments fail before the dial. */ - REQUIRE(wolfcert_est_get_cacerts(&srv, NULL) == WOLFCERT_ERR_BAD_ARG); + REQUIRE(wolfcert_est_get_cacerts(&srv, WOLFCERT_ENCODING_PEM, + NULL) == WOLFCERT_ERR_BAD_ARG); REQUIRE(wolfcert_est_simple_enroll(&srv, NULL, 0, &res) == WOLFCERT_ERR_BAD_ARG); return 0; } +/* An unknown encoding gets BAD_ARG before the dial, with the output cleared. */ +static int test_est_rejects_bad_encoding(void) +{ + WolfCertServerCfg srv = { + .protocol = WOLFCERT_PROTO_EST, + .server_url = "https://127.0.0.1:1/.well-known/est", + .verify_server = 1 + }; + WolfCertBuffer out; + + memset(&out, 0xA5, sizeof(out)); + REQUIRE(wolfcert_est_get_cacerts(&srv, (WolfCertEncoding)2, &out) + == WOLFCERT_ERR_BAD_ARG); + REQUIRE(out.data == NULL && out.len == 0); + return 0; +} + /* A SCEP config gets BAD_ARG from the EST one-shots and the session open. */ static int test_est_rejects_scep_cfg(void) { @@ -354,7 +375,8 @@ static int test_est_rejects_scep_cfg(void) .dev_id = WOLFCERT_DEVID_SOFTWARE }; WolfCertKey* rk = NULL; - REQUIRE(wolfcert_est_get_cacerts(&srv, &out) == WOLFCERT_ERR_BAD_ARG); + REQUIRE(wolfcert_est_get_cacerts(&srv, WOLFCERT_ENCODING_PEM, + &out) == WOLFCERT_ERR_BAD_ARG); REQUIRE(wolfcert_est_get_csr_attrs(&srv, &out) == WOLFCERT_ERR_BAD_ARG); REQUIRE(wolfcert_est_simple_enroll(&srv, dummy_csr, sizeof(dummy_csr), &res) == WOLFCERT_ERR_BAD_ARG); @@ -370,7 +392,8 @@ static int test_est_rejects_scep_cfg(void) /* protocol = 0 is refused too. */ srv.protocol = (WolfCertProtocol)0; - REQUIRE(wolfcert_est_get_cacerts(&srv, &out) == WOLFCERT_ERR_BAD_ARG); + REQUIRE(wolfcert_est_get_cacerts(&srv, WOLFCERT_ENCODING_PEM, + &out) == WOLFCERT_ERR_BAD_ARG); REQUIRE(wolfcert_est_session_open(&srv, &sess) == WOLFCERT_ERR_BAD_ARG); return 0; @@ -495,7 +518,8 @@ static int test_est_uses_cfg_transport(void) srv.transport = tr; g_cfg_tr_connects = 0; - REQUIRE(wolfcert_est_get_cacerts(&srv, &out) != WOLFCERT_OK); + REQUIRE(wolfcert_est_get_cacerts(&srv, WOLFCERT_ENCODING_PEM, + &out) != WOLFCERT_OK); REQUIRE(g_cfg_tr_connects == 1); return 0; } @@ -544,7 +568,8 @@ static int check_empty_body(WOLFSSL_CTX* ctx, const WolfCertServerCfg* tmpl, snprintf(url, sizeof(url), "https://127.0.0.1:%d/.well-known/est", port); srv.server_url = url; - REQUIRE(wolfcert_est_get_cacerts(&srv, &out) == WOLFCERT_ERR_HTTP); + REQUIRE(wolfcert_est_get_cacerts(&srv, WOLFCERT_ENCODING_PEM, + &out) == WOLFCERT_ERR_HTTP); REQUIRE(out.data == NULL); /* A one-shot closes after its request; a session keeps the connection. */ REQUIRE(strstr(sc.request, "Connection: close\r\n") != NULL); @@ -556,7 +581,8 @@ static int check_empty_body(WOLFSSL_CTX* ctx, const WolfCertServerCfg* tmpl, REQUIRE(res.cert_pem.data == NULL); REQUIRE(wolfcert_est_session_open(&srv, &sess) == WOLFCERT_OK); - REQUIRE(wolfcert_est_session_get_cacerts(sess, &out) == WOLFCERT_ERR_HTTP); + REQUIRE(wolfcert_est_session_get_cacerts(sess, WOLFCERT_ENCODING_PEM, + &out) == WOLFCERT_ERR_HTTP); wolfcert_est_session_close(sess); REQUIRE(out.data == NULL); REQUIRE(strstr(sc.request, "Connection: keep-alive\r\n") != NULL); @@ -572,40 +598,39 @@ static int check_empty_body(WOLFSSL_CTX* ctx, const WolfCertServerCfg* tmpl, return 0; } -/* 401 is WOLFCERT_ERR_AUTH from every EST call, and an empty 500 does not - * mean "no CSR attributes". */ +/* An empty /csrattrs reply: a 401 is WOLFCERT_ERR_AUTH, a 500 is not "no CSR + * attributes", and a 200, 405 or 501 is. Every outcome leaves the output + * empty. */ static int check_error_status(WOLFSSL_CTX* ctx, const WolfCertServerCfg* tmpl) { - struct srv_ctx sc = { .ctx = ctx, .status = 401, .conns = 3 }; + static const struct { int status; int rc; } cases[] = { + { 401, WOLFCERT_ERR_AUTH }, { 500, WOLFCERT_ERR_HTTP }, + { 200, WOLFCERT_OK }, { 405, WOLFCERT_OK }, + { 501, WOLFCERT_OK }, + }; WolfCertServerCfg srv = *tmpl; - WolfCertEstSession* sess = NULL; - WolfCertBuffer out = { 0 }; + WolfCertBuffer out; + struct srv_ctx sc; pthread_t tid; char url[128]; int port = 0; - - sc.listen_fd = listen_loopback(&port); - REQUIRE(sc.listen_fd >= 0); - REQUIRE(pthread_create(&tid, NULL, srv_thread, &sc) == 0); - snprintf(url, sizeof(url), "https://127.0.0.1:%d/.well-known/est", port); - srv.server_url = url; - - REQUIRE(wolfcert_est_get_cacerts(&srv, &out) == WOLFCERT_ERR_AUTH); - REQUIRE(wolfcert_est_get_csr_attrs(&srv, &out) == WOLFCERT_ERR_AUTH); - REQUIRE(wolfcert_est_session_open(&srv, &sess) == WOLFCERT_OK); - REQUIRE(wolfcert_est_session_get_cacerts(sess, &out) == WOLFCERT_ERR_AUTH); - wolfcert_est_session_close(sess); - pthread_join(tid, NULL); - - sc = (struct srv_ctx){ .ctx = ctx, .status = 500, .conns = 1 }; - sc.listen_fd = listen_loopback(&port); - REQUIRE(sc.listen_fd >= 0); - REQUIRE(pthread_create(&tid, NULL, srv_thread, &sc) == 0); - snprintf(url, sizeof(url), "https://127.0.0.1:%d/.well-known/est", port); - - REQUIRE(wolfcert_est_get_csr_attrs(&srv, &out) == WOLFCERT_ERR_HTTP); - REQUIRE(out.data == NULL); - pthread_join(tid, NULL); + size_t i; + + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + sc = (struct srv_ctx){ .ctx = ctx, .status = cases[i].status, + .conns = 1 }; + sc.listen_fd = listen_loopback(&port); + REQUIRE(sc.listen_fd >= 0); + REQUIRE(pthread_create(&tid, NULL, srv_thread, &sc) == 0); + snprintf(url, sizeof(url), "https://127.0.0.1:%d/.well-known/est", + port); + srv.server_url = url; + + memset(&out, 0xA5, sizeof(out)); + REQUIRE(wolfcert_est_get_csr_attrs(&srv, &out) == cases[i].rc); + pthread_join(tid, NULL); + REQUIRE(out.data == NULL && out.len == 0); + } return 0; } @@ -629,6 +654,9 @@ int main(void) if (test_est_rejects_scep_cfg()) return 1; + if (test_est_rejects_bad_encoding()) + return 1; + if (test_est_uses_cfg_transport()) return 1; @@ -675,7 +703,8 @@ int main(void) .verify_server = 1 }; WolfCertBuffer ca_pem = { 0 }; - REQUIRE(wolfcert_est_get_cacerts(&srv, &ca_pem) == WOLFCERT_OK); + REQUIRE(wolfcert_est_get_cacerts(&srv, WOLFCERT_ENCODING_PEM, + &ca_pem) == WOLFCERT_OK); REQUIRE(memmem(ca_pem.data, ca_pem.len, "BEGIN CERTIFICATE", 17) != NULL); wolfcert_buffer_free(&ca_pem); diff --git a/tests/unit/test_scep_msg.c b/tests/unit/test_scep_msg.c index ea5373a..28f6510 100644 --- a/tests/unit/test_scep_msg.c +++ b/tests/unit/test_scep_msg.c @@ -1571,6 +1571,7 @@ static int test_next_ca_response_signer_trust(void) /* Accepted when bound to the CA that actually signed it. */ REQUIRE(wolfcert_scep_verify_next_ca_response(resp.data, resp.len, ca_der, ca_len, + WOLFCERT_ENCODING_PEM, &pem, NULL) == WOLFCERT_OK); REQUIRE(pem.len > 0); wolfcert_buffer_free(&pem); @@ -1578,12 +1579,14 @@ static int test_next_ca_response_signer_trust(void) /* Rejected when bound to a different CA. */ REQUIRE(wolfcert_scep_verify_next_ca_response(resp.data, resp.len, att_der, att_len, + WOLFCERT_ENCODING_PEM, &pem, NULL) != WOLFCERT_OK); wolfcert_buffer_free(&pem); /* A NULL current CA is rejected. */ REQUIRE(wolfcert_scep_verify_next_ca_response(resp.data, resp.len, - NULL, 0, &pem, NULL) + NULL, 0, WOLFCERT_ENCODING_PEM, + &pem, NULL) == WOLFCERT_ERR_BAD_ARG); wolfcert_buffer_free(&pem); @@ -2038,6 +2041,35 @@ static int test_scep_builds_before_dial(void) return 0; } +/* An unknown encoding gets BAD_ARG before the dial, with the output cleared. */ +static int test_scep_rejects_bad_encoding(void) +{ + static const uint8_t dummy_ca[] = { 0x30, 0x03, 0x02, 0x01, 0x00 }; + WolfCertServerCfg srv = { .protocol = WOLFCERT_PROTO_SCEP, + .server_url = "http://127.0.0.1:1/scep" }; + WolfCertBuffer out; + + memset(&out, 0xA5, sizeof(out)); + REQUIRE(wolfcert_scep_get_ca_cert(&srv, (WolfCertEncoding)2, &out) + == WOLFCERT_ERR_BAD_ARG); + REQUIRE(out.data == NULL && out.len == 0); + + memset(&out, 0xA5, sizeof(out)); + REQUIRE(wolfcert_scep_get_next_ca_cert(&srv, (WolfCertEncoding)2, + dummy_ca, sizeof(dummy_ca), &out) + == WOLFCERT_ERR_BAD_ARG); + REQUIRE(out.data == NULL && out.len == 0); + + /* So does GetNextCACert without the current CA it is checked against. */ + REQUIRE(wolfcert_scep_get_next_ca_cert(&srv, WOLFCERT_ENCODING_PEM, + NULL, 0, &out) + == WOLFCERT_ERR_BAD_ARG); + REQUIRE(wolfcert_scep_get_next_ca_cert(&srv, WOLFCERT_ENCODING_PEM, + dummy_ca, 0, &out) + == WOLFCERT_ERR_BAD_ARG); + return 0; +} + /* An EST config with Basic credentials gets BAD_ARG from the SCEP GetCA* * calls and the session open. */ static int test_scep_rejects_est_cfg(void) @@ -2054,10 +2086,12 @@ static int test_scep_rejects_est_cfg(void) WolfCertScepSession* sess = NULL; REQUIRE(wolfcert_scep_get_ca_caps(&srv, &caps) == WOLFCERT_ERR_BAD_ARG); - REQUIRE(wolfcert_scep_get_ca_cert(&srv, &out) == WOLFCERT_ERR_BAD_ARG); - REQUIRE(wolfcert_scep_get_ca_cert_enc(&srv, WOLFCERT_ENCODING_DER, &out) + REQUIRE(wolfcert_scep_get_ca_cert(&srv, WOLFCERT_ENCODING_PEM, + &out) == WOLFCERT_ERR_BAD_ARG); + REQUIRE(wolfcert_scep_get_ca_cert(&srv, WOLFCERT_ENCODING_DER, &out) == WOLFCERT_ERR_BAD_ARG); - REQUIRE(wolfcert_scep_get_next_ca_cert(&srv, dummy_ca, sizeof(dummy_ca), + REQUIRE(wolfcert_scep_get_next_ca_cert(&srv, WOLFCERT_ENCODING_PEM, + dummy_ca, sizeof(dummy_ca), &out) == WOLFCERT_ERR_BAD_ARG); REQUIRE(wolfcert_scep_session_open(&srv, &sess) == WOLFCERT_ERR_BAD_ARG); @@ -2886,6 +2920,8 @@ int main(void) REQUIRE(wolfcert_init(test_heap_hint()) == WOLFCERT_OK); if (run_test(test_getca_url)) return 1; + if (run_test(test_scep_rejects_bad_encoding)) + return 1; if (run_test(test_scep_builds_before_dial)) return 1; if (run_test(test_scep_rejects_est_cfg)) diff --git a/wolfcert/est.h b/wolfcert/est.h index 73c351a..9cf4578 100644 --- a/wolfcert/est.h +++ b/wolfcert/est.h @@ -27,16 +27,12 @@ extern "C" { #endif -/* GET /.well-known/est/cacerts - returns the CA chain as PEM. +/* GET /.well-known/est/cacerts - returns the CA chain as PEM for + * WOLFCERT_ENCODING_PEM, or as concatenated DER for WOLFCERT_ENCODING_DER. * A 401 or 403 returns WOLFCERT_ERR_AUTH, any other non-200 _HTTP. */ WOLFCERT_API int wolfcert_est_get_cacerts(const WolfCertServerCfg* srv, - WolfCertBuffer* out_ca_pem); - -/* wolfcert_est_get_cacerts in the requested encoding; DER gives the chain as - * concatenated DER. */ -WOLFCERT_API int wolfcert_est_get_cacerts_enc(const WolfCertServerCfg* srv, - WolfCertEncoding enc, - WolfCertBuffer* out_ca); + WolfCertEncoding enc, + WolfCertBuffer* out_ca); /* GET /.well-known/est/csrattrs - returns the CsrAttrs DER for * wolfcert_est_parse_csr_attrs, empty (NULL, 0) on 204, 404, 405, 501 or a @@ -154,24 +150,27 @@ WOLFCERT_API int wolfcert_est_simple_reenroll(const WolfCertServerCfg* srv, WolfCertEstResult* out); /* Keep-alive EST session carrying several requests over one TLS connection. - * With proto_opts.est.allow_post_handshake_auth set, the server can request - * the preloaded client cert via TLS 1.3 post-handshake auth (RFC 8446 section + * Each call on it is blocking or _nb, and the style may change between + * requests. An _nb call returning WOLFCERT_ERR_WANT_READ / _WANT_WRITE is + * repeated with the same arguments when the session fd is ready. While a + * request is in flight, a call for another operation, output or encoding, and + * any blocking call, returns WOLFCERT_ERR_BAD_ARG and leaves it intact. Open + * checks the config and connects synchronously; it does not verify the server: + * the first request runs the TLS handshake and returns WOLFCERT_ERR_TLS if it + * fails, or WOLFCERT_ERR_MEMORY if wolfSSL runs out of memory. HTTP Basic + * credentials are copied at open and sent on every request. With + * proto_opts.est.allow_post_handshake_auth set, the server can request the + * preloaded client cert via TLS 1.3 post-handshake auth (RFC 8446 section * 4.6.2); open then fails with WOLFCERT_ERR_UNSUPPORTED if wolfSSL lacks - * WOLFSSL_POST_HANDSHAKE_AUTH. HTTP Basic credentials are copied at open and - * sent on every request. Open does not verify the server: the first request - * runs the TLS handshake and returns WOLFCERT_ERR_TLS if it fails, or - * WOLFCERT_ERR_MEMORY if wolfSSL runs out of memory. */ + * WOLFSSL_POST_HANDSHAKE_AUTH. */ typedef struct WolfCertEstSession WolfCertEstSession; WOLFCERT_API int wolfcert_est_session_open(const WolfCertServerCfg* srv, WolfCertEstSession** out); WOLFCERT_API int wolfcert_est_session_get_cacerts(WolfCertEstSession* s, - WolfCertBuffer* out_ca_pem); - -WOLFCERT_API int wolfcert_est_session_get_cacerts_enc(WolfCertEstSession* s, - WolfCertEncoding enc, - WolfCertBuffer* out_ca); + WolfCertEncoding enc, + WolfCertBuffer* out_ca); WOLFCERT_API int wolfcert_est_session_get_csr_attrs(WolfCertEstSession* s, WolfCertBuffer* out_attrs_der); @@ -194,17 +193,9 @@ WOLFCERT_API void wolfcert_est_session_close(WolfCertEstSession* s); * -1 when a caller-supplied WolfCertTransport backs it (no descriptor). */ WOLFCERT_API int wolfcert_est_session_fd(const WolfCertEstSession* s); -/* Non-blocking variants. Each _nb call returns WOLFCERT_OK once the output is - * populated, or WOLFCERT_ERR_WANT_READ / _WANT_WRITE to be repeated with the - * same arguments when the fd is ready; any other value ends the request. While - * a _nb request is in flight, any other request on the session returns - * WOLFCERT_ERR_BAD_ARG and leaves it intact. */ WOLFCERT_API int wolfcert_est_session_get_cacerts_nb(WolfCertEstSession* s, - WolfCertBuffer* out_ca_pem); - -WOLFCERT_API int wolfcert_est_session_get_cacerts_enc_nb(WolfCertEstSession* s, - WolfCertEncoding enc, - WolfCertBuffer* out_ca); + WolfCertEncoding enc, + WolfCertBuffer* out_ca); WOLFCERT_API int wolfcert_est_session_get_csr_attrs_nb(WolfCertEstSession* s, WolfCertBuffer* out_attrs_der); diff --git a/wolfcert/scep.h b/wolfcert/scep.h index 024d175..2302119 100644 --- a/wolfcert/scep.h +++ b/wolfcert/scep.h @@ -46,16 +46,14 @@ typedef struct { WOLFCERT_API int wolfcert_scep_get_ca_caps(const WolfCertServerCfg* srv, WolfCertScepCaps* out_caps); +/* GET ?operation=GetCACert - returns the CA cert(s) as PEM for + * WOLFCERT_ENCODING_PEM, or for WOLFCERT_ENCODING_DER the whole bundle as + * concatenated DER, usable as ca_bundle for the enroll and GetNextCACert + * calls. An application/x-x509-ca-ra-cert response or a signedData body under + * any Content-Type is read as a bundle. */ WOLFCERT_API int wolfcert_scep_get_ca_cert(const WolfCertServerCfg* srv, - WolfCertBuffer* out_ca_pem); - -/* wolfcert_scep_get_ca_cert in the requested encoding. DER yields the whole - * GetCACert bundle as concatenated DER, usable as ca_bundle for the enroll - * and GetNextCACert calls. An application/x-x509-ca-ra-cert response or a - * signedData body under any Content-Type is read as a bundle. */ -WOLFCERT_API int wolfcert_scep_get_ca_cert_enc(const WolfCertServerCfg* srv, - WolfCertEncoding enc, - WolfCertBuffer* out_ca); + WolfCertEncoding enc, + WolfCertBuffer* out_ca); /* Digest for wolfcert_scep_verify_ca_fingerprint. AUTO picks it from the * fingerprint length (20 SHA-1, 32 SHA-256, 64 SHA-512), so a 20-byte value @@ -81,7 +79,7 @@ WOLFCERT_API int wolfcert_scep_verify_ca_fingerprint(const uint8_t* ca_der, WolfCertScepFpAlg alg); /* The certificate in a GetCACert bundle (concatenated DER, as from - * wolfcert_scep_get_ca_cert_enc) to envelope requests to; *cert points into + * wolfcert_scep_get_ca_cert in DER) to envelope requests to; *cert points into * bundle, and heap NULL = default. Nothing here is verified, so bundle should * hold only certificates the caller trusts, such as a pinned CA and the RAs * it issued. A non-CA RSA certificate is preferred: @@ -132,11 +130,14 @@ WOLFCERT_API void wolfcert_scep_result_free(WolfCertScepResult* r); /* PKCSReq. The challengePassword travels in the CSR, from * meta.challenge_password at wolfcert_csr_build; SCEP sends no HTTP - * credentials. ra_cert is the DER cert the request is enveloped to; ca_bundle - * is the trusted GetCACert bundle the CertRep signer is checked against, which - * for a single-cert CA is ra_cert itself. Returns WOLFCERT_OK once a CertRep - * verifies, with SUCCESS / PENDING / FAILURE in out->status; TLS, HTTP and - * parse failures return their error code. */ + * credentials. caps is the GetCACaps reply; NULL sends a POST with SHA-256 and + * 3DES where proto_opts.scep leaves the hash and cipher on AUTO, and returns + * WOLFCERT_ERR_UNSUPPORTED if wolfSSL lacks 3DES. ra_cert is the DER cert the + * request is enveloped to; ca_bundle is the trusted GetCACert bundle the + * CertRep signer is checked against, which for a single-cert CA is ra_cert + * itself. Returns WOLFCERT_OK once a CertRep verifies, with SUCCESS / + * PENDING / FAILURE in out->status; TLS, HTTP and parse failures return their + * error code. */ WOLFCERT_API int wolfcert_scep_pkcs_req(const WolfCertServerCfg* srv, const WolfCertScepCaps* caps, const uint8_t* ra_cert, size_t ra_cert_len, @@ -146,7 +147,7 @@ WOLFCERT_API int wolfcert_scep_pkcs_req(const WolfCertServerCfg* srv, WolfCertScepResult* out); /* RenewalReq: current_key signs the pkiMessage and csr_der carries the new - * public key. ra_cert and ca_bundle are as for wolfcert_scep_pkcs_req. */ + * public key. caps, ra_cert and ca_bundle are as for wolfcert_scep_pkcs_req. */ WOLFCERT_API int wolfcert_scep_renewal_req(const WolfCertServerCfg* srv, const WolfCertScepCaps* caps, const uint8_t* ra_cert, size_t ra_cert_len, @@ -161,8 +162,8 @@ WOLFCERT_API int wolfcert_scep_renewal_req(const WolfCertServerCfg* srv, * self-signed cert is rebuilt from signer_key), or the cert being renewed for * a RenewalReq. * transaction_id is the prior request's, echoed verbatim; a value outside - * PrintableString returns WOLFCERT_ERR_BAD_ARG. ra_cert and ca_bundle are as - * for wolfcert_scep_pkcs_req. */ + * PrintableString returns WOLFCERT_ERR_BAD_ARG. caps, ra_cert and ca_bundle + * are as for wolfcert_scep_pkcs_req. */ WOLFCERT_API int wolfcert_scep_get_cert_initial(const WolfCertServerCfg* srv, const WolfCertScepCaps* caps, const uint8_t* ra_cert, size_t ra_cert_len, @@ -178,7 +179,7 @@ WOLFCERT_API int wolfcert_scep_get_cert_initial(const WolfCertServerCfg* srv, * e.g. to recover a lost local copy. GetCert is optional, so a CA may answer * FAILURE/badCertId or not at all. signer_cert / signer_key sign the * pkiMessage, with no self-signed fallback. serial is the INTEGER content - * (DecodedCert.serial / serialSz). ra_cert and ca_bundle are as for + * (DecodedCert.serial / serialSz). caps, ra_cert and ca_bundle are as for * wolfcert_scep_pkcs_req. On a hit out->status is SUCCESS; a verified reply * without the requested issuer and serial returns WOLFCERT_ERR_PROTOCOL with * out->status UNSET, and running out of memory searching it returns @@ -192,23 +193,28 @@ WOLFCERT_API int wolfcert_scep_get_cert(const WolfCertServerCfg* srv, const uint8_t* serial, size_t serial_len, WolfCertScepResult* out); -/* GetNextCACert (RFC 8894 section 4.7): fetch the roll-over CA cert. Returns - * WOLFCERT_ERR_NOT_FOUND on HTTP 404 (no roll-over). current_ca_der is - * required: the current, verified CA cert(s) as concatenated DER. The reply is - * rejected unless its signer shares a public key with one of them. */ +/* GetNextCACert (RFC 8894 section 4.7): fetch the roll-over CA cert, as PEM or + * concatenated DER per enc. Returns WOLFCERT_ERR_NOT_FOUND on HTTP 404 (no + * roll-over). current_ca_der is required: the current, verified CA cert(s) as + * concatenated DER. The reply is rejected unless its signer shares a public + * key with one of them. */ WOLFCERT_API int wolfcert_scep_get_next_ca_cert(const WolfCertServerCfg* srv, + WolfCertEncoding enc, const uint8_t* current_ca_der, size_t current_ca_len, - WolfCertBuffer* out_next_ca_pem); + WolfCertBuffer* out_next_ca); -/* Keep-alive SCEP session carrying several PKIOperation round trips; GetCACaps - * and GetCACert stay one-shot calls. Plain http:// is accepted and https:// - * needs srv->verify_server. A _nb call returning WANT_READ / WANT_WRITE is - * repeated with the same arguments; a call with a different out pointer - * returns WOLFCERT_ERR_BAD_ARG. DNS and the initial connect stay synchronous. - * Over https:// open does not verify the server: the first request runs the - * TLS handshake and returns WOLFCERT_ERR_TLS if it fails, or - * WOLFCERT_ERR_MEMORY if wolfSSL runs out of memory. */ +/* Keep-alive SCEP session carrying several requests over one connection. + * Plain http:// is accepted and https:// needs srv->verify_server. Each call + * on it is blocking or _nb, and the style may change between requests. Open + * checks the config, copies proto_opts.scep and connects synchronously; over + * https:// it does not verify the server: the first request runs the TLS + * handshake and returns WOLFCERT_ERR_TLS if it fails, or WOLFCERT_ERR_MEMORY + * if wolfSSL runs out of memory. An _nb call returning WOLFCERT_ERR_WANT_READ + * / _WANT_WRITE is repeated with the same arguments when the session fd is + * ready. While a request is in flight, a call for another operation, output + * or encoding, and any blocking call, returns WOLFCERT_ERR_BAD_ARG and leaves + * it intact. */ typedef struct WolfCertScepSession WolfCertScepSession; WOLFCERT_API int wolfcert_scep_session_open(const WolfCertServerCfg* srv, @@ -223,23 +229,23 @@ WOLFCERT_API int wolfcert_scep_session_get_ca_caps(WolfCertScepSession* s, WolfCertScepCaps* out_caps); WOLFCERT_API int wolfcert_scep_session_get_ca_caps_nb(WolfCertScepSession* s, WolfCertScepCaps* out_caps); -WOLFCERT_API int wolfcert_scep_session_get_ca_cert_enc(WolfCertScepSession* s, - WolfCertEncoding enc, - WolfCertBuffer* out_ca); -WOLFCERT_API int wolfcert_scep_session_get_ca_cert_enc_nb(WolfCertScepSession* s, - WolfCertEncoding enc, - WolfCertBuffer* out_ca); +WOLFCERT_API int wolfcert_scep_session_get_ca_cert(WolfCertScepSession* s, + WolfCertEncoding enc, + WolfCertBuffer* out_ca); +WOLFCERT_API int wolfcert_scep_session_get_ca_cert_nb(WolfCertScepSession* s, + WolfCertEncoding enc, + WolfCertBuffer* out_ca); WOLFCERT_API int wolfcert_scep_session_get_next_ca_cert(WolfCertScepSession* s, + WolfCertEncoding enc, const uint8_t* current_ca_der, size_t current_ca_len, - WolfCertBuffer* out_next_ca_pem); + WolfCertBuffer* out_next_ca); WOLFCERT_API int wolfcert_scep_session_get_next_ca_cert_nb(WolfCertScepSession* s, + WolfCertEncoding enc, const uint8_t* current_ca_der, size_t current_ca_len, - WolfCertBuffer* out_next_ca_pem); + WolfCertBuffer* out_next_ca); -/* PKCSReq over the session; arguments and result as for - * wolfcert_scep_pkcs_req. */ WOLFCERT_API int wolfcert_scep_session_pkcs_req(WolfCertScepSession* s, const WolfCertScepCaps* caps, const uint8_t* ra_cert, size_t ra_cert_len, @@ -253,7 +259,6 @@ WOLFCERT_API int wolfcert_scep_session_pkcs_req_nb(WolfCertScepSession* s, const WolfCertKey* new_key, const uint8_t* csr_der, size_t csr_der_len, WolfCertScepResult* out); -/* RenewalReq over the session; see wolfcert_scep_renewal_req. */ WOLFCERT_API int wolfcert_scep_session_renewal_req(WolfCertScepSession* s, const WolfCertScepCaps* caps, const uint8_t* ra_cert, size_t ra_cert_len, @@ -269,7 +274,6 @@ WOLFCERT_API int wolfcert_scep_session_renewal_req_nb(WolfCertScepSession* s, const WolfCertKey* current_key, const uint8_t* csr_der, size_t csr_der_len, WolfCertScepResult* out); -/* GetCertInitial over the session; see wolfcert_scep_get_cert_initial. */ WOLFCERT_API int wolfcert_scep_session_get_cert_initial(WolfCertScepSession* s, const WolfCertScepCaps* caps, const uint8_t* ra_cert, size_t ra_cert_len, diff --git a/zephyr/tests/wolfcert_est/src/main.c b/zephyr/tests/wolfcert_est/src/main.c index 15e100c..4468792 100644 --- a/zephyr/tests/wolfcert_est/src/main.c +++ b/zephyr/tests/wolfcert_est/src/main.c @@ -98,7 +98,7 @@ static void* est_setup(void) zassert_unreachable("wolfcert_init"); return NULL; } - rc = wolfcert_est_get_cacerts(&g_cfg, &g_ca_pem); + rc = wolfcert_est_get_cacerts(&g_cfg, WOLFCERT_ENCODING_PEM, &g_ca_pem); if (rc != WOLFCERT_OK) { printk("wolfcert_est: get_cacerts rc=%d (%s) wolfssl=%d: %s\n", rc, wolfcert_strerror(rc), wolfcert_last_wolfssl_err(),