aboutsummaryrefslogtreecommitdiff
path: root/src/supplemental/tls/wolfssl
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2025-10-05 16:51:15 -0700
committerGarrett D'Amore <garrett@damore.org>2025-10-05 20:56:39 -0700
commit06d6d80f8c92ef1d3bd7c00c919e10a411183cb3 (patch)
treeedf8d4cff9b2f595ccd9e3cb4db3cf31eb13bc02 /src/supplemental/tls/wolfssl
parentd1bd64c8251171ac8e1d4e71ab8726c2a64fd55a (diff)
downloadnng-06d6d80f8c92ef1d3bd7c00c919e10a411183cb3.tar.gz
nng-06d6d80f8c92ef1d3bd7c00c919e10a411183cb3.tar.bz2
nng-06d6d80f8c92ef1d3bd7c00c919e10a411183cb3.zip
fixes #2173 New TLS cert API - replaces the properties for CN and ALTNAMES.
This will replace the NNG_OPT_TLS_PEER_ALTNAMES and NNG_OPT_TLS_PEER_CN properties, and gives a bit more access to the certificate, as well as direct access to the raw DER form, which should allow use in other APIs.
Diffstat (limited to 'src/supplemental/tls/wolfssl')
-rw-r--r--src/supplemental/tls/wolfssl/CMakeLists.txt8
-rw-r--r--src/supplemental/tls/wolfssl/wolfssl.c328
2 files changed, 296 insertions, 40 deletions
diff --git a/src/supplemental/tls/wolfssl/CMakeLists.txt b/src/supplemental/tls/wolfssl/CMakeLists.txt
index 762757a3..e580860e 100644
--- a/src/supplemental/tls/wolfssl/CMakeLists.txt
+++ b/src/supplemental/tls/wolfssl/CMakeLists.txt
@@ -60,7 +60,13 @@ if (NNG_TLS_ENGINE STREQUAL "wolf")
if (NNG_WOLFSSL_HAVE_PEER_CERT)
nng_defines(NNG_WOLFSSL_HAVE_PEER_CERT)
else ()
- message(STATUS "wolfSSL configured without peer cert chain support.")
+ message(STATUS "wolfSSL configured without full certificate APIs.")
+ message(NOTICE "
+ ************************************************************
+ WolfSSL functionality for working with certificates is missing.
+ Consider rebuilding WolfSSL for full functionality.
+ ************************************************************")
+
endif ()
if (NNG_WOLFSSL_HAVE_PSK)
diff --git a/src/supplemental/tls/wolfssl/wolfssl.c b/src/supplemental/tls/wolfssl/wolfssl.c
index a18c1605..ef3f7391 100644
--- a/src/supplemental/tls/wolfssl/wolfssl.c
+++ b/src/supplemental/tls/wolfssl/wolfssl.c
@@ -43,6 +43,13 @@ struct nng_tls_engine_conn {
int auth_mode;
};
+struct nng_tls_engine_cert {
+ WOLFSSL_X509 *crt;
+ char *subject;
+ char *issuer;
+ char serial[64]; // maximum binary serial is 20 bytes
+};
+
typedef struct psk {
// NB: Technically RFC 4279 requires this be UTF-8 string, although
// others suggest making it opaque bytes. We treat it as a C string,
@@ -281,53 +288,47 @@ wolf_conn_verified(nng_tls_engine_conn *ec)
}
}
-static char *
-wolf_conn_peer_cn(nng_tls_engine_conn *ec)
+static nng_err
+wolf_conn_peer_cert(nng_tls_engine_conn *ec, nng_tls_engine_cert **certp)
{
#ifdef NNG_WOLFSSL_HAVE_PEER_CERT
- WOLFSSL_X509 *cert;
- char *cn;
+ nng_tls_engine_cert *cert;
- if ((cert = wolfSSL_get_peer_certificate(ec->ssl)) == NULL) {
- return (NULL);
+ WOLFSSL_X509 *wc;
+ if ((wc = wolfSSL_get_peer_certificate(ec->ssl)) == NULL) {
+ return (NNG_ENOENT);
}
- cn = wolfSSL_X509_get_subjectCN(cert);
- if (cn != NULL) {
- cn = nng_strdup(cn);
+ if ((cert = nni_zalloc(sizeof(*cert))) == NULL) {
+ wolfSSL_X509_free(wc);
+ return (NNG_ENOMEM);
}
- return (cn);
+ cert->crt = wc;
+ *certp = cert;
+ return (NNG_OK);
#else
- return (NULL);
+ NNI_ARG_UNUSED(ec);
+ NNI_ARG_UNUSED(certp);
+ return (NNG_ENOTSUP);
#endif
}
-static char **
-wolf_conn_peer_alt_names(nng_tls_engine_conn *ec)
+static char *
+wolf_conn_peer_cn(nng_tls_engine_conn *ec)
{
#ifdef NNG_WOLFSSL_HAVE_PEER_CERT
WOLFSSL_X509 *cert;
- int num = 0;
- char **names;
+ char *cn;
if ((cert = wolfSSL_get_peer_certificate(ec->ssl)) == NULL) {
return (NULL);
}
- while (wolfSSL_X509_get_next_altname(cert) != NULL) {
- num++;
- }
- if ((names = nni_zalloc(sizeof(char *) * num)) == NULL) {
- return (NULL);
- }
- if ((cert = wolfSSL_get_peer_certificate(ec->ssl)) == NULL) {
- nni_free(names, sizeof(char *) * num);
- return (NULL);
- }
- for (int i = 0; i < num; i++) {
- names[i] = wolfSSL_X509_get_next_altname(cert);
- NNI_ASSERT(names[i] != NULL);
+ cn = wolfSSL_X509_get_subjectCN(cert);
+ if (cn != NULL) {
+ cn = nng_strdup(cn);
}
- return (names);
+ return (cn);
#else
+ NNI_ARG_UNUSED(ec);
return (NULL);
#endif
}
@@ -678,6 +679,240 @@ wolf_config_version(nng_tls_engine_config *cfg, nng_tls_version min_ver,
}
static void
+wolf_cert_free(nng_tls_engine_cert *cert)
+{
+#ifdef NNG_WOLFSSL_HAVE_PEER_CERT
+ if (cert->subject != NULL) {
+ wolfSSL_Free(cert->subject);
+ }
+ if (cert->issuer != NULL) {
+ wolfSSL_Free(cert->issuer);
+ }
+ if (cert->crt != NULL) {
+ wolfSSL_X509_free(cert->crt);
+ }
+ nni_free(cert, sizeof(*cert));
+#else
+ NNI_ARG_UNUSED(cert);
+#endif
+}
+
+// In struct nng_tls_engine_cert_ops_s
+static nng_err
+wolf_cert_get_der(nng_tls_engine_cert *cert, uint8_t *buf, size_t *sz)
+{
+#ifdef NNG_WOLFSSL_HAVE_PEER_CERT
+ const uint8_t *der;
+ int derSz;
+ der = wolfSSL_X509_get_der(cert->crt, &derSz);
+ if (*sz < (size_t) derSz) {
+ *sz = (size_t) derSz;
+ return (NNG_ENOSPC);
+ }
+ *sz = (size_t) derSz;
+ memcpy(buf, der, *sz);
+ return (NNG_OK);
+#else
+ NNI_ARG_UNUSED(cert);
+ NNI_ARG_UNUSED(buf);
+ NNI_ARG_UNUSED(sz);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+static nng_err
+wolf_cert_parse_der(
+ nng_tls_engine_cert **crtp, const uint8_t *der, size_t size)
+{
+#ifdef NNG_WOLFSSL_HAVE_PEER_CERT
+ WOLFSSL_X509 *x;
+ nng_tls_engine_cert *cert;
+
+ if ((cert = nni_zalloc(sizeof(*cert))) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ if ((cert->crt = wolfSSL_X509_d2i(&x, der, size)) == NULL) {
+ nni_free(cert, sizeof(*cert));
+ return (NNG_ENOMEM);
+ }
+ *crtp = cert;
+ return (NNG_OK);
+#else
+ NNI_ARG_UNUSED(crtp);
+ NNI_ARG_UNUSED(der);
+ NNI_ARG_UNUSED(size);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+static nng_err
+wolf_cert_parse_pem(nng_tls_engine_cert **crtp, const char *pem, size_t size)
+{
+ nng_err rv;
+ uint8_t *derBuf;
+ int derSize;
+
+ // DER files are smaller than PEM (PEM is base64 encoded and includes
+ // headers)
+ if ((derBuf = nni_alloc(size)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+
+ derSize = wc_CertPemToDer(
+ (const uint8_t *) pem, size, derBuf, size, 0 /* cert type */);
+ if (derSize < 0) {
+ nni_free(derBuf, size);
+ return (NNG_ECRYPTO);
+ }
+
+ rv = wolf_cert_parse_der(crtp, derBuf, derSize);
+ nni_free(derBuf, size);
+ return (rv);
+}
+
+static nng_err
+wolf_cert_subject(nng_tls_engine_cert *cert, char **subject)
+{
+#ifdef NNG_WOLFSSL_HAVE_PEER_CERT
+ WOLFSSL_X509_NAME *xn;
+
+ if (cert->subject != NULL) {
+ *subject = cert->subject;
+ return (NNG_OK);
+ }
+
+ xn = wolfSSL_X509_get_subject_name(cert->crt);
+ if (xn == NULL) {
+ return (NNG_ENOENT);
+ }
+ cert->subject = wolfSSL_X509_NAME_oneline(xn, NULL, 0);
+ *subject = cert->subject;
+ return (NNG_OK);
+#else
+ NNI_ARG_UNUSED(cert);
+ NNI_ARG_UNUSED(subject);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+static nng_err
+wolf_cert_issuer(nng_tls_engine_cert *cert, char **issuer)
+{
+#ifdef NNG_WOLFSSL_HAVE_PEER_CERT
+ WOLFSSL_X509_NAME *xn;
+
+ if (cert->issuer != NULL) {
+ *issuer = cert->issuer;
+ return (NNG_OK);
+ }
+
+ xn = wolfSSL_X509_get_issuer_name(cert->crt);
+ if (xn == NULL) {
+ return (NNG_ENOENT);
+ }
+ cert->issuer = wolfSSL_X509_NAME_oneline(xn, NULL, 0);
+ *issuer = cert->issuer;
+ return (NNG_OK);
+#else
+ NNI_ARG_UNUSED(cert);
+ NNI_ARG_UNUSED(issuer);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+static nng_err
+wolf_cert_serial(nng_tls_engine_cert *cert, char **serial)
+{
+#ifdef NNG_WOLFSSL_HAVE_PEER_CERT
+ uint8_t num[20]; // max is 20 bytes per RFC
+ char *s;
+ int len;
+
+ if (cert->serial[0] != 0) {
+ *serial = cert->serial;
+ return (NNG_OK);
+ }
+
+ len = sizeof(num);
+ wolfSSL_X509_get_serial_number(cert->crt, num, &len);
+
+ s = cert->serial;
+ for (int i = 0; i < len; i++) {
+ snprintf(s, 4, "%s%02X", i > 0 ? ":" : "", num[i]);
+ s += strlen(s);
+ }
+ *serial = cert->serial;
+ return (NNG_OK);
+#else
+ NNI_ARG_UNUSED(cert);
+ NNI_ARG_UNUSED(serial);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+static nng_err
+wolf_cert_subject_cn(nng_tls_engine_cert *cert, char **cn)
+{
+#ifdef NNG_WOLFSSL_HAVE_PEER_CERT
+ *cn = wolfSSL_X509_get_subjectCN(cert->crt);
+ if (*cn == NULL) {
+ return (NNG_ENOENT);
+ }
+ return (NNG_OK);
+#else
+ NNI_ARG_UNUSED(cert);
+ NNI_ARG_UNUSED(cn);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+static nng_err
+wolf_cert_next_alt(nng_tls_engine_cert *cert, char **alt)
+{
+#ifdef NNG_WOLFSSL_HAVE_PEER_CERT
+ *alt = wolfSSL_X509_get_next_altname(cert->crt);
+ if (*alt == NULL) {
+ return (NNG_ENOENT);
+ }
+ return (NNG_OK);
+#else
+ NNI_ARG_UNUSED(cert);
+ NNI_ARG_UNUSED(alt);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+static nng_err
+wolf_cert_not_before(nng_tls_engine_cert *cert, struct tm *tmp)
+{
+#ifdef NNG_WOLFSSL_HAVE_PEER_CERT
+ WOLFSSL_ASN1_TIME *when;
+ when = wolfSSL_X509_get_notBefore(cert->crt);
+ wolfSSL_ASN1_TIME_to_tm(when, tmp);
+ return (NNG_OK);
+#else
+ NNI_ARG_UNUSED(cert);
+ NNI_ARG_UNUSED(tmp);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+static nng_err
+wolf_cert_not_after(nng_tls_engine_cert *cert, struct tm *tmp)
+{
+#ifdef NNG_WOLFSSL_HAVE_PEER_CERT
+ WOLFSSL_ASN1_TIME *when;
+ when = wolfSSL_X509_get_notAfter(cert->crt);
+ wolfSSL_ASN1_TIME_to_tm(when, tmp);
+ return (NNG_OK);
+#else
+ NNI_ARG_UNUSED(cert);
+ NNI_ARG_UNUSED(tmp);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+static void
wolf_logging_cb(const int level, const char *msg)
{
switch (level) {
@@ -742,22 +977,37 @@ static nng_tls_engine_config_ops wolf_config_ops = {
};
static nng_tls_engine_conn_ops wolf_conn_ops = {
- .size = sizeof(nng_tls_engine_conn),
- .init = wolf_conn_init,
- .fini = wolf_conn_fini,
- .close = wolf_conn_close,
- .recv = wolf_conn_recv,
- .send = wolf_conn_send,
- .handshake = wolf_conn_handshake,
- .verified = wolf_conn_verified,
- .peer_cn = wolf_conn_peer_cn,
- .peer_alt_names = wolf_conn_peer_alt_names,
+ .size = sizeof(nng_tls_engine_conn),
+ .init = wolf_conn_init,
+ .fini = wolf_conn_fini,
+ .close = wolf_conn_close,
+ .recv = wolf_conn_recv,
+ .send = wolf_conn_send,
+ .handshake = wolf_conn_handshake,
+ .verified = wolf_conn_verified,
+ .peer_cn = wolf_conn_peer_cn,
+ .peer_cert = wolf_conn_peer_cert,
+};
+
+static nng_tls_engine_cert_ops wolf_cert_ops = {
+ .fini = wolf_cert_free,
+ .get_der = wolf_cert_get_der,
+ .parse_der = wolf_cert_parse_der,
+ .parse_pem = wolf_cert_parse_pem,
+ .subject = wolf_cert_subject,
+ .issuer = wolf_cert_issuer,
+ .serial_number = wolf_cert_serial,
+ .subject_cn = wolf_cert_subject_cn,
+ .next_alt_name = wolf_cert_next_alt,
+ .not_before = wolf_cert_not_before,
+ .not_after = wolf_cert_not_after,
};
nng_tls_engine nng_tls_engine_ops = {
.version = NNG_TLS_ENGINE_VERSION,
.config_ops = &wolf_config_ops,
.conn_ops = &wolf_conn_ops,
+ .cert_ops = &wolf_cert_ops,
.name = "wolf",
.description = "wolfSSL " LIBWOLFSSL_VERSION_STRING,
.init = tls_engine_init,