Repository navigation
Conversation
An optional callback that reports what happened while wolfSSL checked a certificate or CRL, for security logging. Today an application gets one error code for a whole chain and cannot tell which certificate, field or check failed. Built with --enable-cert-events (WOLFSSL_CERT_EVENTS, also in CMake and --enable-all); off by default. When off, no struct or ABI changes. Each WOLFSSL_CERT_EVENT says: - kind: DEFECT for each failed certificate or CRL check, RESULT when a verification ends (0 when it passed, so a DEFECT then a passed RESULT means the verify callback allowed it), INFO with each certificate's algorithms and key size, and NOTICE for a strict check that fired without failing. - the certificate's role (peer, chain CA, trust anchor, CRL, CRL signer) and chain depth, the RFC 5280 field, the reason and the error code. - a stable condition value for each kind of failure, named after the IEC 62351-9 and IEC 62351-3 audit events. - the extension OID as content octets, since OID sums depend on the build. - which certificate: its subject, issuer and serial number, and the TLS connection (WOLFSSL*) whose handshake raised the event. The callback only observes; the verify callback still decides. Set with wolfSSL_CertManagerSetCertEventCb(), wolfSSL_CTX_SetCertEventCb(), wolfSSL_X509_STORE_SetCertEventCb() or wc_SetCertEventCallback(). Events come from TLS peer chains, CertManagerVerify*, X509_verify_cert(), trust anchors loaded with verification, parsing without a certificate manager, CRL checks and CRL loading, with both ASN.1 parsers. Optional strict profile checks report a unique ID, no AKID keyIdentifier, a CA without key usage, no CRL distribution point, no OCSP responder and a CRL thisUpdate in the future. Each can be enforced with wolfSSL_CertManagerSetCertEventStrict() and friends, failing with its own error, ASN_STRICT_*_E (-1043 to -1048). They run after every other check, also when the verify callback allows another failure, so allowing one failure never skips another check. WC_CEV_STRICT_ALL means every check the build can run. Self-signed certificates are exempt from the issuer checks. Tests: certs/test/gen-cert-events.py generates certs/test/cert-events/, certificates, chains and CRLs with one fault each, valid until 2049. tests/api/test_certman.c covers each condition over the certificate manager, TLS, X509_verify_cert(), trust anchors and CRLs. Two Linux CI entries build the feature without the compatibility layer.
|
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical findings allow strict-error overrides to bypass additional enforced checks and revocation validation, or report a successful CA load without installing the CA.
6 open issues
Prevent strict errors from overriding failed CA loads · New Run context CRL and date checks before strict-error overrides · New Require approval for every enforced strict-check failure · New Enable certificate events by default in the all-features bundle · New Log CRL signature and signer errors before normalization · New Report actual bit sizes for non-byte-aligned curves · New
What changed in this PR
Adds optional certificate and CRL validation events to wolfSSL for structured security logging, alongside configurable strict-profile checks.
Changes:
- Introduces event callbacks with stable condition codes, certificate identity, algorithm details, and verification outcomes.
- Integrates reporting and strict checks into certificate parsing, TLS, certificate-manager, X509-store, and CRL paths.
- Adds build options, API documentation, test registrations, certificate fixtures, and CI configurations.
| File | Description |
|---|---|
wolfssl/wolfcrypt/error-crypt.h |
Defines six strict-check error codes. |
wolfssl/wolfcrypt/asn.h |
Adds parser event state and helper declarations. |
wolfssl/wolfcrypt/asn_public.h |
Defines public event types and strict-check flags. |
wolfssl/ssl.h |
Declares event callback and enforcement APIs. |
wolfssl/internal.h |
Adds certificate-manager and handshake event state. |
wolfcrypt/src/error.c |
Adds strict-check error strings. |
wolfcrypt/src/asn.c |
Implements event reporting and strict certificate checks. |
wolfcrypt/src/asn_orig.c |
Adds original-parser failure reporting. |
tests/api/test_certman.h |
Registers certificate-event tests. |
src/x509_str.c |
Integrates events into X509-store verification. |
src/ssl_certman.c |
Adds manager APIs and verification events. |
src/ssl_api_cert.c |
Exposes TLS-context configuration APIs. |
src/internal.c |
Reports TLS certificate events and deferred status results. |
src/crl.c |
Adds CRL events and strict thisUpdate checks. |
doc/dox_comments/header_files/ssl.h |
Documents manager, context, and store APIs. |
doc/dox_comments/header_files/asn_public.h |
Documents standalone parser APIs. |
configure.ac |
Adds the Autotools feature option. |
CMakeLists.txt |
Adds the CMake feature option. |
cmake/options.h.in |
Exports the event feature define. |
ChangeLog.md |
Describes validation events and strict checks. |
certs/test/include.am |
Includes event fixtures in source distributions. |
certs/test/gen-cert-events.py |
Generates certificate, chain, and CRL fixtures. |
certs/test/cert-events/chain-signer-not-ca.pem |
Tests a signer without CA status. |
certs/test/cert-events/chain-signer-no-certsign.pem |
Tests missing certificate-signing permission. |
certs/test/cert-events/chain-pathlen-exceeded.pem |
Tests a path-length violation. |
certs/test/cert-events/chain-leaf-ku-no-digsig.pem |
Tests missing leaf digital-signature usage. |
certs/test/cert-events/chain-leaf-eku-client-only.pem |
Tests a server leaf restricted to client authentication. |
certs/test/cert-events/chain-good.pem |
Provides a valid reference chain. |
certs/test/catalog.txt |
Describes fixtures and regeneration. |
.github/configs/os-check-linux.json |
Adds three certificate-event CI configurations. |
Review effort: Balanced
| case WC_NO_ERR_TRACE(ASN_STRICT_UNIQUE_ID_E): | ||
| case WC_NO_ERR_TRACE(ASN_STRICT_NO_AKID_E): | ||
| case WC_NO_ERR_TRACE(ASN_STRICT_NO_KEY_USAGE_E): | ||
| case WC_NO_ERR_TRACE(ASN_STRICT_NO_CRL_DP_E): | ||
| case WC_NO_ERR_TRACE(ASN_STRICT_NO_OCSP_E): |
| if ((ret == 0) && (crlRet == 0)) { | ||
| ret = wc_CertEventStrictChecks(cert); |
| if (enforced && (ret == 0)) { | ||
| ret = c->error; |
| add_option("WOLFSSL_CERT_EVENTS" | ||
| "Enable structured certificate and CRL validation event callback (default: disabled)" | ||
| "no" "yes;no") |
| case WC_NO_ERR_TRACE(ASN_CRL_CONFIRM_E): | ||
| wc_CertEventFailureCond(cert, ret, WC_CEV_FIELD_SIGNATURE, | ||
| WC_CEV_REASON_SIGNATURE, WC_CEV_COND_CRL_SIG_INVALID); |
| if (curveId >= 0) { | ||
| int sz = wc_ecc_get_curve_size_from_id(curveId); | ||
| if (sz > 0) { | ||
| bits = sz * 8; |
|
this is too big to review as one 5.3k line commit, can you split it into a stack? rough cut: 1) event struct, cm/ctx setters, defect+result on the tls peer chain and CertManagerVerify, template asn.c only, with the sel conditions. 2) x509_store, trust anchor load, no-cm parsing. 3) crl. 4) info events. 5) asn_orig parity. 6) strict checks and the 6 new error codes, those change accept/reject so they need their own review and maybe aren't needed for zd 21876. i'll review 1 first. after your vacation is fine |


Description
Adds an optional callback that reports what happened while wolfSSL checked a
certificate or CRL. It is for security logging: an application can log each
failed check with a stable code. Regulations and standards such as the EU
Cyber Resilience Act and IEC 62351 ask for this kind of logging. Today the
application gets one error code for a whole chain and cannot tell which
certificate, field or check failed.
Fixes zd#21876
Built with
--enable-cert-events(WOLFSSL_CERT_EVENTS); off by default,included in
--enable-all. When off, no struct field or ABI changes; onlythe six new error codes exist.
What the callback receives
One
WOLFSSL_CERT_EVENTper event:the chain, trust anchor, CRL, CRL signer) and chain depth, the RFC 5280
field, the reason (malformed, missing, unsupported, expired, signature,
no signer, revoked, policy, constraint), and the wolfSSL error code.
DEFECT followed by a passed RESULT means the verify callback allowed it.
A TLS 1.2 client that asked for the server's certificate status sends it
once that status was checked at ServerHelloDone, since revocation can
still fail there.
size in bits, so the application can apply its own approved list.
Every event carries a
conditionvalue: a stable key for each kind offailure, named after the IEC 62351-9 and IEC 62351-3 audit events. Extension
OIDs come as content octets as well as OID sums, because the sums depend on
the build.
Each event also says which certificate it is about (subject, issuer, serial
number, when decoded) and, during a handshake, the
WOLFSSL*connection, soa log line can be tied to a peer. Host name and OCSP failures appear only in
the RESULT's error for now.
The callback only observes. The verify callback still decides; nothing about
accepting or rejecting a certificate changes unless a strict check is
enforced.
API
wolfSSL_CertManagerSetCertEventCb(),wolfSSL_CTX_SetCertEventCb(),wolfSSL_X509_STORE_SetCertEventCb(),wc_SetCertEventCallback()(nocertificate manager).
wolfSSL_CertManagerSetCertEventStrict(),wolfSSL_CTX_SetCertEventStrict(),wolfSSL_X509_STORE_SetCertEventStrict(),wc_SetCertEventStrict().Paths covered
TLS peer chains (each certificate with its depth),
CertManagerVerify*,X509_verify_cert(), trust anchors loaded with verification on, parsingwithout a certificate manager, CRL checks of a certificate, and CRL loading.
Strict profile checks
Unique IDs present, no AKID, a CA without key usage, no CRL distribution
point, no OCSP responder, and a CRL thisUpdate in the future. Each one is
reported as a NOTICE. When its
wc_CertEventStrictbit is set, it sends aDEFECT and fails with its own error,
ASN_STRICT_*_E(-1043 to -1048, witherror strings). The checks run only when a callback or an enforced check is
set, and last: when every other check passed, or once the verify callback
allowed what failed, in which case the strict failure goes to the verify
callback too. So allowing a strict failure does not skip host name, key
usage, chain length or revocation checks, and allowing one of those does
not skip the strict checks.
WC_CEV_STRICT_ALLmeans everycheck the build can run; naming one the build cannot run returns
NOT_COMPILED_IN. Trust anchors loaded with verification fail to load whenan enforced check fires. They do not overlap
WOLFSSL_NO_ASN_STRICT, whichonly relaxes existing rejections.
FIPS
The hooks are in asn.c, asn_orig.c, crl.c, internal.c, ssl_certman.c,
x509_str.c and ssl_api_cert.c. No FIPS module source changed. New error
codes are added to error-crypt.h span 2 and error.c.
Planned follow-ups (separate PRs)
version and serial number, names, time encoding, empty or wrongly
critical extensions, CA consistency, subject alternative names, the TLS
leaf and the CRL profile. Same pattern as here: a NOTICE by default, and
enforce bits grouped by area.
about a certificate (IEC 62351-3).
Found while checking the RFC 5280 rules, to be fixed on their own: a
certificate version value of 3 is accepted, and a valid critical
certificatePolicies, policyConstraints or inhibitAnyPolicy extension is
rejected in the default build.
Testing
certs/test/gen-cert-events.py(Python 3 and the openssl command line)generates
certs/test/cert-events/: copies ofserver-cert.derwith onedefect each, chains with one fault each, CRLs with one fault each, and
the strict-check certificates. All valid until 2049.
tests/api/test_certman.c, one or more for every condition,over the certificate manager, TLS (memio),
X509_verify_cert(), trustanchors, parsing without a certificate manager, and CRLs. Includes
regression tests that allowing a strict failure does not skip the host
name check, that allowing another failure does not skip the strict
checks, and that the RESULT waits for the TLS 1.2 certificate status.
--enable-cert-events --enable-crl; thesame with
--enable-asn=original,--enable-smallstackplusWOLFSSL_NO_CRL_DATE_CHECKandIGNORE_KEY_EXTENSIONS,--enable-debug-trace-errcodes, andWOLFSSL_X509_TINY;--enable-cert-eventswithout CRL, also withWOLFSSL_X509_TINY; C89with
-Werror;--enable-all(full API suite). Built only:NO_ASN_TIME,--enable-cryptonly, and events off.os-check-linux.json:cert-events-crl,cert-events-crl-asn-originalandcert-events-tiny.--enable-allentries already cover the rest.
Checklist