Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
478a7fc
Add WOLFSSL_CHAIN_VERIFY_CB to replace peer chain verification
julek-wolfssl Sep 3, 2026
002afb8
Fix chain verify callback build without stapling and test without cli…
julek-wolfssl Sep 3, 2026
93cb2de
Address chain verify callback review and fix the no-client build
julek-wolfssl Sep 3, 2026
7305c07
Guard clear-chain-certs test helper like its only caller
julek-wolfssl Sep 3, 2026
d25dd94
Wrap two lines over 80 columns
julek-wolfssl Sep 4, 2026
dd3771e
Hand the chain verify callback the certificate array directly
julek-wolfssl Sep 4, 2026
d5d3d1b
Keep a suspended Certificate intact across wolfSSL_write()
julek-wolfssl Sep 4, 2026
081a2fd
Document what accepting means and re-check the callback's restrictions
julek-wolfssl Sep 7, 2026
143a994
Let an accepted chain clear an earlier verify result
julek-wolfssl Sep 7, 2026
79955ee
Declare startIdx in every suspend build
julek-wolfssl Sep 10, 2026
5695d65
Address review: compile IsHsSuspendErr() only with handshake suspend
julek-wolfssl Oct 1, 2026
b5b96f7
Address review: fail certificates whose decode stops part way
julek-wolfssl Oct 2, 2026
6ee36ef
Address review: keep a deferred chain verdict across wolfSSL_shutdown()
julek-wolfssl Oct 2, 2026
5cca7c7
Address review: alert on peer key errors with the chain verify callback
julek-wolfssl Oct 2, 2026
34c6ff4
Address review: document server-side client auth with the callback
julek-wolfssl Oct 2, 2026
e68d71d
Address review: drop blank line in cmake options list
julek-wolfssl Oct 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/configs/os-check-linux.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@
"CPPFLAGS=-DWOLFSSL_PUBLIC_MP -DWOLFSSL_DEBUG_NONBLOCK"]},
{"name": "all-asn-template", "minutes": 3.5,
"configure": ["--enable-all", "--enable-asn=template"]},
{"name": "chain-verify-cb-async", "minutes": 3.5,
"comment": "chain verify callback with async crypt, the third handshake-suspend feature. Also the only leg with async crypt and CRL but without WOLFSSL_NONBLOCK_OCSP.",
"configure": ["--enable-chain-verify-cb", "--enable-asynccrypt",
"--enable-asynccrypt-sw", "--enable-ocsp", "--enable-crl",
"--enable-opensslextra", "--enable-sessioncerts"]},
{"name": "tls13-dtls13-session-misc", "minutes": 3.3,
"configure": ["--enable-tls13", "--enable-session-ticket", "--enable-dtls",
"--enable-dtls13", "--enable-opensslextra", "--enable-sessioncerts",
Expand All @@ -102,6 +107,15 @@
{"name": "dtls13-ocspstapling-cert-cb", "minutes": 3.1,
"configure": ["--enable-dtls", "--enable-dtls13", "--enable-ocspstapling",
"--enable-ocspstapling2", "--enable-cert-setup-cb", "--enable-sessioncerts"]},
{"name": "chain-verify-cb", "minutes": 3.1,
"comment": "postauth, stapling and rpk exercise the callback's resume-from-read and fail-hard paths.",
"configure": ["--enable-chain-verify-cb", "--enable-opensslextra",
"--enable-sessioncerts", "--enable-smallstack", "--enable-postauth",
"--enable-ocspstapling", "--enable-rpk"]},
{"name": "chain-verify-cb-nonblock-ocsp", "minutes": 3.1,
"comment": "chain verify callback alongside the other handshake-suspend feature, and DTLS.",
"configure": ["--enable-chain-verify-cb", "--enable-ocsp", "--enable-crl",
"--enable-dtls", "CPPFLAGS=-DWOLFSSL_NONBLOCK_OCSP"]},
{"name": "nullcipher-tls13-in-default", "minutes": 3.0,
"comment": "Legacy WOLFSSL_TLS13_NULL_CIPHER_IN_DEFAULT mode: RFC 9150 integrity-only suites back in the default TLS 1.3/DTLS 1.3 cipher list. The only config that runs tests/test-tls13-null.conf and tests/test-dtls13-null.conf (they need the suites in the default list). The dtls-cid-renego-psk entry covers the default-exclusion mode of --enable-nullcipher.",
"configure": ["--enable-dtls", "--enable-dtls13", "--enable-nullcipher",
Expand Down
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1904,6 +1904,11 @@ add_option("WOLFSSL_CERTGENCACHE"
"Enable decoded cert caching (default: disabled)"
"no" "yes;no")

# Chain verify callback
add_option("WOLFSSL_CHAIN_VERIFY_CB"
"Enable replacing peer certificate verification with a user callback (default: disabled)"
"no" "yes;no")

# HKDF
add_option("WOLFSSL_HKDF"
"Enable HKDF (HMAC-KDF) support (default: disabled)"
Expand Down Expand Up @@ -3493,6 +3498,10 @@ if(WOLFSSL_CERTGENCACHE)
list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_CERT_GEN_CACHE")
endif()

if(WOLFSSL_CHAIN_VERIFY_CB)
list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_CHAIN_VERIFY_CB")
endif()

if(WOLFSSL_CRYPTOCB)
list(APPEND WOLFSSL_DEFINITIONS "-DWOLF_CRYPTO_CB")
endif()
Expand Down
2 changes: 2 additions & 0 deletions cmake/options.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,8 @@ extern "C" {
#cmakedefine WOLFSSL_ALWAYS_VERIFY_CB
#undef WOLFSSL_CERT_SETUP_CB
#cmakedefine WOLFSSL_CERT_SETUP_CB
#undef WOLFSSL_CHAIN_VERIFY_CB
#cmakedefine WOLFSSL_CHAIN_VERIFY_CB
#undef WOLFSSL_CIPHER_INTERNALNAME
#cmakedefine WOLFSSL_CIPHER_INTERNALNAME
#undef WOLFSSL_DER_LOAD
Expand Down
11 changes: 11 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -1554,6 +1554,7 @@ then
test "$enable_savesession" = "" && enable_savesession=yes
test "$enable_savecert" = "" && enable_savecert=yes
test "$enable_postauth" = "" && enable_postauth=yes
test "$enable_chain_verify_cb" = "" && enable_chain_verify_cb=yes
test "$enable_hrrcookie" = "" && enable_hrrcookie=yes
test "$enable_crl_monitor" = "" && enable_crl_monitor=yes
test "$enable_sni" = "" && enable_sni=yes
Expand Down Expand Up @@ -12869,6 +12870,13 @@ AC_ARG_ENABLE([cert-setup-cb],
[ ENABLED_CERT_SETUP_CB=no ]
)

# Replaces peer certificate chain verification with an application callback
AC_ARG_ENABLE([chain-verify-cb],
[AS_HELP_STRING([--enable-chain-verify-cb],[Enable replacing peer certificate verification with a user callback (default: disabled)])],
[ ENABLED_CHAIN_VERIFY_CB=$enableval ],
[ ENABLED_CHAIN_VERIFY_CB=no ]
)

# check if should run the trusted peer certs test
# (for now checking both C_FLAGS and C_EXTRA_FLAGS)
AS_CASE(["$CFLAGS $CPPFLAGS"],[*'WOLFSSL_TRUST_PEER_CERT'*],[ENABLED_TRUSTED_PEER_CERT=yes])
Expand Down Expand Up @@ -13406,6 +13414,9 @@ AS_IF([test "x$ENABLED_RPK" = "xyes"],
AS_IF([test "x$ENABLED_CERT_SETUP_CB" = "xyes"],
[AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_CERT_SETUP_CB"])

AS_IF([test "x$ENABLED_CHAIN_VERIFY_CB" = "xyes"],
[AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_CHAIN_VERIFY_CB"])

AS_IF([test "x$ENABLED_ALTNAMES" = "xyes"],
[AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_ALT_NAMES"])

Expand Down
166 changes: 166 additions & 0 deletions doc/dox_comments/header_files/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3330,6 +3330,172 @@ void wolfSSL_SetCertCbCtx(WOLFSSL* ssl, void* ctx);
*/
void wolfSSL_CTX_SetCertCbCtx(WOLFSSL_CTX* ctx, void* userCtx);

/*!
\ingroup CertsKeys

\brief Replaces wolfSSL's verification of the peer's certificate chain with
an application callback, for every SSL/TLS object created from the context.
When a callback is set, wolfSSL decodes the certificates from the
Certificate message and hands them to the callback as raw DER, the peer's
own certificate first. It builds no chain, verifies no signature and checks
no date, revocation status, key usage or host name, and the verify callback
set with wolfSSL_CTX_set_verify() is not called; the callback is consulted
for any certificate the peer sends, even under WOLFSSL_VERIFY_NONE. A
server only requests a client certificate under WOLFSSL_VERIFY_PEER and
only requires one with WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, so a server
that relies on the callback for client authentication must set both.
Malformed DER, a certificate the parser refuses regardless of the verify
mode such as one with a zero serial number, or one whose decoding stops
part way such as one with an unsupported key algorithm, still fails the
handshake before the callback is called. An unknown critical extension
or a key usage inconsistent with the basic constraints is left for the
callback to judge. The callback returns 0 to accept, CHAIN_VERIFY_WANT_E
to suspend the handshake until the application re-enters
wolfSSL_connect(), wolfSSL_accept(),
wolfSSL_read() or wolfSSL_write() (a certificate received after the
handshake is resumed by wolfSSL_read() only, and wolfSSL_write() fails with
CHAIN_VERIFY_WANT_E until then), or any other value to reject with
CHAIN_VERIFY_CB_E and a fatal bad_certificate alert; 0 is the only
accepting value, so returning WOLFSSL_SUCCESS rejects. Because wolfSSL
checks nothing itself, wolfSSL_get_verify_result() reports
WOLFSSL_X509_V_OK once the callback accepts, recording its verdict rather
than a verification wolfSSL performed. DTLS, raw public keys and verifying
a stapled OCSP response are not supported with the callback: setting it on
a context configured for one of them fails, and so does the handshake of a
connection using one of them, with CHAIN_VERIFY_UNSUPPORTED_E. The
stapling check covers the client, the side that verifies a stapled
response; a server may still staple its own status. Requires
WOLFSSL_CHAIN_VERIFY_CB (--enable-chain-verify-cb).

\return WOLFSSL_SUCCESS on success.
\return BAD_FUNC_ARG when ctx is NULL.
\return CHAIN_VERIFY_UNSUPPORTED_E when the context uses a DTLS method,
raw public keys or client-side OCSP stapling.

\param ctx pointer to the SSL context, created with wolfSSL_CTX_new().
\param cb the callback, or NULL to clear it.

_Example_
\code
static int myChainVerify(WOLFSSL* ssl, const WOLFSSL_BUFFER_INFO* certs,
int certsSz, void* ctx)
{
// certs[0] is the peer's certificate, the rest is the chain it sent
if (!hsmVerifyStarted(certs, certsSz))
return CHAIN_VERIFY_WANT_E; // ask again later
return hsmVerifyPassed() ? 0 : -1;
}
...
WOLFSSL_CTX* ctx = wolfSSL_CTX_new(method);
if (wolfSSL_CTX_SetChainVerifyCb(ctx, myChainVerify) != WOLFSSL_SUCCESS) {
// context uses DTLS, raw public keys or client-side stapling
}
\endcode

\sa wolfSSL_SetChainVerifyCb
\sa wolfSSL_CTX_SetChainVerifyCtx
\sa wolfSSL_SetChainVerifyCtx
\sa wolfSSL_GetChainVerifyCtx
*/
int wolfSSL_CTX_SetChainVerifyCb(WOLFSSL_CTX* ctx, ChainVerifyCb cb);

/*!
\ingroup CertsKeys

\brief Sets the chain verification callback for one SSL/TLS object. It
takes precedence over the callback set on the context with
wolfSSL_CTX_SetChainVerifyCb(), which documents the callback's contract.

\return WOLFSSL_SUCCESS on success.
\return BAD_FUNC_ARG when ssl is NULL.
\return CHAIN_VERIFY_UNSUPPORTED_E when the object uses DTLS, raw public
keys or client-side OCSP stapling.

\param ssl pointer to the SSL session, created with wolfSSL_new().
\param cb the callback, or NULL to fall back to the context's.

_Example_
\code
WOLFSSL* ssl = wolfSSL_new(ctx);
if (wolfSSL_SetChainVerifyCb(ssl, myChainVerify) != WOLFSSL_SUCCESS) {
// object uses DTLS, raw public keys or client-side stapling
}
\endcode

\sa wolfSSL_CTX_SetChainVerifyCb
\sa wolfSSL_SetChainVerifyCtx
*/
int wolfSSL_SetChainVerifyCb(WOLFSSL* ssl, ChainVerifyCb cb);

/*!
\ingroup CertsKeys

\brief Sets the user context passed to the chain verification callback
for every SSL/TLS object created from the context.

\return none No return.

\param ctx pointer to the SSL context, created with wolfSSL_CTX_new().
\param userCtx the value the callback receives as its ctx argument.

_Example_
\code
WOLFSSL_CTX* ctx = wolfSSL_CTX_new(method);
wolfSSL_CTX_SetChainVerifyCb(ctx, myChainVerify);
wolfSSL_CTX_SetChainVerifyCtx(ctx, &myHsm);
\endcode

\sa wolfSSL_CTX_SetChainVerifyCb
\sa wolfSSL_SetChainVerifyCtx
\sa wolfSSL_GetChainVerifyCtx
*/
void wolfSSL_CTX_SetChainVerifyCtx(WOLFSSL_CTX* ctx, void* userCtx);

/*!
\ingroup CertsKeys

\brief Sets the user context passed to the chain verification callback
for one SSL/TLS object. It takes precedence over the value set on the
context with wolfSSL_CTX_SetChainVerifyCtx().

\return none No return.

\param ssl pointer to the SSL session, created with wolfSSL_new().
\param ctx the value the callback receives as its ctx argument, or NULL
to fall back to the context's.

_Example_
\code
WOLFSSL* ssl = wolfSSL_new(ctx);
wolfSSL_SetChainVerifyCtx(ssl, &myConnectionState);
\endcode

\sa wolfSSL_CTX_SetChainVerifyCtx
\sa wolfSSL_GetChainVerifyCtx
*/
void wolfSSL_SetChainVerifyCtx(WOLFSSL* ssl, void* ctx);

/*!
\ingroup CertsKeys

\brief Returns the user context the chain verification callback is
called with for this SSL/TLS object: the value set with
wolfSSL_SetChainVerifyCtx() when there is one, otherwise the context's.

\return void* the user context, or NULL when none is set or ssl is NULL.

\param ssl pointer to the SSL session, created with wolfSSL_new().

_Example_
\code
struct myState* st = (struct myState*)wolfSSL_GetChainVerifyCtx(ssl);
\endcode

\sa wolfSSL_SetChainVerifyCtx
\sa wolfSSL_CTX_SetChainVerifyCtx
*/
void* wolfSSL_GetChainVerifyCtx(const WOLFSSL* ssl);

/*!
\ingroup IO

Expand Down
2 changes: 2 additions & 0 deletions examples/configs/user_settings_all.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ extern "C" {
#define HAVE_OID_ENCODING
#define WOLFSSL_ASN_TEMPLATE
#define WOLFSSL_ALT_NAMES /* Support subject alternative names extension */
/* Replace peer chain verification with an application callback */
#define WOLFSSL_CHAIN_VERIFY_CB

/* Certificate Revocation */
#define HAVE_OCSP
Expand Down
Loading
Loading