net: l2: wifi: Add support for run-time certificates

Using TLS credentials library add support for run-time certificates where
the installed certs are retrieved from the credential store (as of now
only volatile backend is tested).

This helps in production environments.

Implements #79564.

Signed-off-by: Chaitanya Tata <Chaitanya.Tata@nordicsemi.no>
This commit is contained in:
Chaitanya Tata 2025-03-26 19:50:43 +05:30 committed by Benjamin Cabé
commit b2e7d7fc0c
3 changed files with 285 additions and 26 deletions

View file

@ -30,11 +30,15 @@ Wi-Fi PSA crypto supported build
To enable PSA crypto API supported Wi-Fi build, the :kconfig:option:`CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ALT` and the :kconfig:option:`CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_MBEDTLS_PSA` need to be set.
Wi-Fi Enterprise test: X.509 Certificate header generation
**********************************************************
Wi-Fi Enterprise test: X.509 Certificate management
***************************************************
Wi-Fi enterprise security requires use of X.509 certificates, test certificates
in PEM format are committed to the repo at :zephyr_file:`samples/net/wifi/test_certs` and the during the
Wi-Fi enterprise security requires use of X.509 certificates, two methods of installing certificates are supported:
Compile time certificates
-------------------------
Test certificates in PEM format are committed to the repo at :zephyr_file:`samples/net/wifi/test_certs` and the during the
build process the certificates are converted to a C header file that is included by the Wi-Fi shell
module.
@ -55,6 +59,12 @@ For using variable size network buffer, the following overlay file can be used:
$ west build -p -b <board> samples/net/wifi -- -DEXTRA_CONF_FILE=overlay-enterprise-variable-bufs.conf
Run time certificates
---------------------
The Wi-Fi shell module uses TLS credentials subsystem to store and manage the certificates. The certificates can be added at runtime using the shell commands, see :ref:`tls_credentials_shell` for more details.
The sample or application need to enable the :kconfig:option:`CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES` option to use this feature.
To initiate Wi-Fi connection, the following command can be used:

View file

@ -125,3 +125,27 @@ config WIFI_ENT_IDENTITY_MAX_USERS
default 8
help
This option defines the maximum number of identity users allowed connection.
if WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE
config WIFI_SHELL_RUNTIME_CERTIFICATES
bool "Provide Wi-Fi enterprise security certificates at run-time"
select TLS_CREDENTIALS
select TLS_CREDENTIALS_SHELL
select BASE64
help
This option enables providing Wi-Fi enterprise security certificates at run-time.
Uses the TLS credentials subsystem to store and manage the certificates.
if WIFI_SHELL_RUNTIME_CERTIFICATES
config HEAP_MEM_POOL_ADD_SIZE_WIFI_CERT
int "Wi-Fi enterprise security certificates memory pool size"
# STA - 6 certs and each assume 1500 bytes
default 12000
help
The size of the memory pool used by the Wi-Fi enterprise security certificates.
endif # WIFI_SHELL_RUNTIME_CERTIFICATES
endif # WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE

View file

@ -30,7 +30,29 @@ LOG_MODULE_REGISTER(net_wifi_shell, LOG_LEVEL_INF);
#include "net_shell_private.h"
#include <math.h>
#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE
#if defined CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE || \
defined CONFIG_WIFI_NM_HOSTAPD_CRYPTO_ENTERPRISE
#ifdef CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES
#include <zephyr/net/tls_credentials.h>
enum wifi_enterprise_cert_sec_tags {
WIFI_CERT_CA_SEC_TAG = 0x1020001,
WIFI_CERT_CLIENT_KEY_SEC_TAG,
WIFI_CERT_SERVER_KEY_SEC_TAG,
WIFI_CERT_CLIENT_SEC_TAG,
WIFI_CERT_SERVER_SEC_TAG,
/* Phase 2 */
WIFI_CERT_CA_P2_SEC_TAG,
WIFI_CERT_CLIENT_KEY_P2_SEC_TAG,
WIFI_CERT_CLIENT_P2_SEC_TAG,
};
struct wifi_cert_data {
enum tls_credential_type type;
uint32_t sec_tag;
uint8_t **data;
size_t *len;
};
#else
static const char ca_cert_test[] = {
#include <wifi_enterprise_test_certs/ca.pem.inc>
'\0'
@ -67,7 +89,8 @@ static const char server_key_test[] = {
#include <wifi_enterprise_test_certs/server-key.pem.inc>
'\0'
};
#endif
#endif /* CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES */
#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE */
#define WIFI_SHELL_MODULE "wifi"
@ -105,6 +128,12 @@ static struct {
};
uint8_t all;
};
#if defined CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE || \
defined CONFIG_WIFI_NM_HOSTAPD_CRYPTO_ENTERPRISE
#ifdef CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES
struct wifi_enterprise_creds_params enterprise_creds_params;
#endif /* CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES */
#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE */
} context;
static struct net_mgmt_event_callback wifi_shell_mgmt_cb;
@ -121,27 +150,212 @@ static struct wifi_ap_sta_node sta_list[CONFIG_WIFI_SHELL_MAX_AP_STA];
#if defined CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE || \
defined CONFIG_WIFI_NM_HOSTAPD_CRYPTO_ENTERPRISE
static int cmd_wifi_set_enterprise_creds(const struct shell *sh, struct net_if *iface)
#ifdef CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES
static int process_certificates(struct wifi_cert_data *certs, size_t cert_count)
{
for (size_t i = 0; i < cert_count; i++) {
int err;
size_t len = 0;
uint8_t *cert_tmp;
err = tls_credential_get(certs[i].sec_tag, certs[i].type, NULL, &len);
if (err != -EFBIG) {
LOG_ERR("Failed to get credential tag: %d length, err: %d",
certs[i].sec_tag, err);
return err;
}
cert_tmp = k_malloc(len);
if (!cert_tmp) {
LOG_ERR("Failed to allocate memory for credential tag: %d",
certs[i].sec_tag);
return -ENOMEM;
}
err = tls_credential_get(certs[i].sec_tag, certs[i].type, cert_tmp, &len);
if (err) {
LOG_ERR("Failed to get credential tag: %d", certs[i].sec_tag);
k_free(cert_tmp);
return err;
}
*certs[i].data = cert_tmp;
*certs[i].len = len;
}
return 0;
}
static void set_enterprise_creds_params(struct wifi_enterprise_creds_params *params,
bool is_ap)
{
struct wifi_cert_data certs_common[] = {
{
.type = TLS_CREDENTIAL_CA_CERTIFICATE,
.sec_tag = WIFI_CERT_CA_SEC_TAG,
.data = &params->ca_cert,
.len = &params->ca_cert_len,
},
};
struct wifi_cert_data certs_sta[] = {
{
.type = TLS_CREDENTIAL_PRIVATE_KEY,
.sec_tag = WIFI_CERT_CLIENT_KEY_SEC_TAG,
.data = &params->client_key,
.len = &params->client_key_len,
},
{
.type = TLS_CREDENTIAL_PUBLIC_CERTIFICATE,
.sec_tag = WIFI_CERT_CLIENT_SEC_TAG,
.data = &params->client_cert,
.len = &params->client_cert_len,
},
{
.type = TLS_CREDENTIAL_CA_CERTIFICATE,
.sec_tag = WIFI_CERT_CA_P2_SEC_TAG,
.data = &params->ca_cert2,
.len = &params->ca_cert2_len,
},
{
.type = TLS_CREDENTIAL_PRIVATE_KEY,
.sec_tag = WIFI_CERT_CLIENT_KEY_P2_SEC_TAG,
.data = &params->client_key2,
.len = &params->client_key2_len,
},
{
.type = TLS_CREDENTIAL_PUBLIC_CERTIFICATE,
.sec_tag = WIFI_CERT_CLIENT_P2_SEC_TAG,
.data = &params->client_cert2,
.len = &params->client_cert2_len,
},
};
struct wifi_cert_data certs_ap[] = {
{
.type = TLS_CREDENTIAL_PUBLIC_CERTIFICATE,
.sec_tag = WIFI_CERT_SERVER_SEC_TAG,
.data = &params->server_cert,
.len = &params->server_cert_len,
},
{
.type = TLS_CREDENTIAL_PRIVATE_KEY,
.sec_tag = WIFI_CERT_SERVER_KEY_SEC_TAG,
.data = &params->server_key,
.len = &params->server_key_len,
},
};
memset(params, 0, sizeof(*params));
/* Process common certificates */
if (process_certificates(certs_common, ARRAY_SIZE(certs_common)) != 0) {
goto cleanup;
}
/* Process STA-specific certificates */
if (!is_ap) {
if (process_certificates(certs_sta, ARRAY_SIZE(certs_sta)) != 0) {
goto cleanup;
}
}
/* Process AP-specific certificates if is_ap is true */
if (is_ap) {
if (process_certificates(certs_ap, ARRAY_SIZE(certs_ap)) != 0) {
goto cleanup;
}
}
memcpy(&context.enterprise_creds_params, params, sizeof(*params));
return;
cleanup:
for (size_t i = 0; i < ARRAY_SIZE(certs_common); i++) {
if (certs_common[i].data) {
k_free(*certs_common[i].data);
}
}
if (!is_ap) {
for (size_t i = 0; i < ARRAY_SIZE(certs_sta); i++) {
if (certs_sta[i].data) {
k_free(*certs_sta[i].data);
}
}
}
if (is_ap) {
for (size_t i = 0; i < ARRAY_SIZE(certs_ap); i++) {
if (certs_ap[i].data) {
k_free(*certs_ap[i].data);
}
}
}
}
static void clear_enterprise_creds_params(struct wifi_enterprise_creds_params *params)
{
size_t i;
if (!params) {
return;
}
const uint8_t *certs[] = {
params->ca_cert,
params->client_cert,
params->client_key,
params->server_cert,
params->server_key,
params->ca_cert2,
params->client_cert2,
params->client_key2,
};
for (i = 0; i < ARRAY_SIZE(certs); i++) {
k_free((void *)certs[i]);
}
memset(params, 0, sizeof(*params));
}
#else
static void set_enterprise_creds_params(struct wifi_enterprise_creds_params *params,
bool is_ap)
{
params->ca_cert = (uint8_t *)ca_cert_test;
params->ca_cert_len = ARRAY_SIZE(ca_cert_test);
if (!is_ap) {
params->client_cert = (uint8_t *)client_cert_test;
params->client_cert_len = ARRAY_SIZE(client_cert_test);
params->client_key = (uint8_t *)client_key_test;
params->client_key_len = ARRAY_SIZE(client_key_test);
params->ca_cert2 = (uint8_t *)ca_cert2_test;
params->ca_cert2_len = ARRAY_SIZE(ca_cert2_test);
params->client_cert2 = (uint8_t *)client_cert2_test;
params->client_cert2_len = ARRAY_SIZE(client_cert2_test);
params->client_key2 = (uint8_t *)client_key2_test;
params->client_key2_len = ARRAY_SIZE(client_key2_test);
return;
}
params->server_cert = (uint8_t *)server_cert_test;
params->server_cert_len = ARRAY_SIZE(server_cert_test);
params->server_key = (uint8_t *)server_key_test;
params->server_key_len = ARRAY_SIZE(server_key_test);
}
#endif /* CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES */
static int wifi_set_enterprise_creds(const struct shell *sh, struct net_if *iface,
bool is_ap)
{
struct wifi_enterprise_creds_params params = {0};
params.ca_cert = (uint8_t *)ca_cert_test;
params.ca_cert_len = ARRAY_SIZE(ca_cert_test);
params.client_cert = (uint8_t *)client_cert_test;
params.client_cert_len = ARRAY_SIZE(client_cert_test);
params.client_key = (uint8_t *)client_key_test;
params.client_key_len = ARRAY_SIZE(client_key_test);
params.ca_cert2 = (uint8_t *)ca_cert2_test;
params.ca_cert2_len = ARRAY_SIZE(ca_cert2_test);
params.client_cert2 = (uint8_t *)client_cert2_test;
params.client_cert2_len = ARRAY_SIZE(client_cert2_test);
params.client_key2 = (uint8_t *)client_key2_test;
params.client_key2_len = ARRAY_SIZE(client_key2_test);
params.server_cert = (uint8_t *)server_cert_test;
params.server_cert_len = ARRAY_SIZE(server_cert_test);
params.server_key = (uint8_t *)server_key_test;
params.server_key_len = ARRAY_SIZE(server_key_test);
#ifdef CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES
clear_enterprise_creds_params(&context.enterprise_creds_params);
#endif /* CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES */
set_enterprise_creds_params(&params, is_ap);
if (net_mgmt(NET_REQUEST_WIFI_ENTERPRISE_CREDS, iface, &params, sizeof(params))) {
PR_WARNING("Set enterprise credentials failed\n");
return -1;
@ -967,7 +1181,7 @@ static int cmd_wifi_connect(const struct shell *sh, size_t argc,
cnx_params.security == WIFI_SECURITY_TYPE_EAP_PEAP_GTC ||
cnx_params.security == WIFI_SECURITY_TYPE_EAP_TTLS_MSCHAPV2 ||
cnx_params.security == WIFI_SECURITY_TYPE_EAP_PEAP_TLS) {
cmd_wifi_set_enterprise_creds(sh, iface);
wifi_set_enterprise_creds(sh, iface, 0);
}
#endif
@ -1009,6 +1223,11 @@ static int cmd_wifi_disconnect(const struct shell *sh, size_t argc,
PR("Disconnect requested\n");
}
#ifdef CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES
/* Clear the certificates */
clear_enterprise_creds_params(&context.enterprise_creds_params);
#endif /* CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES */
return 0;
}
@ -1979,7 +2198,7 @@ static int cmd_wifi_ap_enable(const struct shell *sh, size_t argc,
cnx_params.security == WIFI_SECURITY_TYPE_EAP_PEAP_GTC ||
cnx_params.security == WIFI_SECURITY_TYPE_EAP_TTLS_MSCHAPV2 ||
cnx_params.security == WIFI_SECURITY_TYPE_EAP_PEAP_TLS) {
cmd_wifi_set_enterprise_creds(sh, iface);
wifi_set_enterprise_creds(sh, iface, 1);
}
#endif
@ -2010,6 +2229,12 @@ static int cmd_wifi_ap_disable(const struct shell *sh, size_t argc,
}
PR("AP mode disable requested\n");
#ifdef CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES
/* Clear the certificates */
clear_enterprise_creds_params(&context.enterprise_creds_params);
#endif /* CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES */
return 0;
}