1 /*
2  * Copyright (c) 2025 Nordic Semiconductor ASA.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_wifi_certs, CONFIG_NET_L2_WIFI_MGMT_LOG_LEVEL);
9 
10 #include <zephyr/net/wifi_certs.h>
11 #include <utils/common.h>
12 #include <eap_peer/eap_config.h>
13 #include <ctrl_iface_zephyr.h>
14 #include <wpa_supplicant/config.h>
15 #include <supp_main.h>
16 
17 static struct wifi_enterprise_creds_params enterprise_creds_params = { 0 };
18 
19 #ifdef CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES
20 #include <zephyr/net/tls_credentials.h>
21 enum wifi_enterprise_cert_sec_tags {
22 	WIFI_CERT_CA_SEC_TAG = 0x1020001,
23 	WIFI_CERT_CLIENT_KEY_SEC_TAG,
24 	WIFI_CERT_SERVER_KEY_SEC_TAG,
25 	WIFI_CERT_CLIENT_SEC_TAG,
26 	WIFI_CERT_SERVER_SEC_TAG,
27 	/* Phase 2 */
28 	WIFI_CERT_CA_P2_SEC_TAG,
29 	WIFI_CERT_CLIENT_KEY_P2_SEC_TAG,
30 	WIFI_CERT_CLIENT_P2_SEC_TAG,
31 };
32 
33 struct wifi_cert_data {
34 	enum tls_credential_type type;
35 	uint32_t sec_tag;
36 	uint8_t **data;
37 	size_t *len;
38 };
39 #else
40 static const char ca_cert_test[] = {
41 	#include <wifi_enterprise_test_certs/ca.pem.inc>
42 	'\0'
43 };
44 
45 static const char client_cert_test[] = {
46 	#include <wifi_enterprise_test_certs/client.pem.inc>
47 	'\0'
48 };
49 
50 static const char client_key_test[] = {
51 	#include <wifi_enterprise_test_certs/client-key.pem.inc>
52 	'\0'
53 };
54 
55 static const char ca_cert2_test[] = {
56 	#include <wifi_enterprise_test_certs/ca2.pem.inc>
57 	'\0'};
58 
59 static const char client_cert2_test[] = {
60 	#include <wifi_enterprise_test_certs/client2.pem.inc>
61 	'\0'};
62 
63 static const char client_key2_test[] = {
64 	#include <wifi_enterprise_test_certs/client-key2.pem.inc>
65 	'\0'};
66 
67 static const char server_cert_test[] = {
68 	#include <wifi_enterprise_test_certs/server.pem.inc>
69 	'\0'
70 };
71 
72 static const char server_key_test[] = {
73 	#include <wifi_enterprise_test_certs/server-key.pem.inc>
74 	'\0'
75 };
76 #endif /* CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES */
77 
78 #ifdef CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES
process_certificates(struct wifi_cert_data * certs,size_t cert_count)79 static int process_certificates(struct wifi_cert_data *certs, size_t cert_count)
80 {
81 	for (size_t i = 0; i < cert_count; i++) {
82 		int err;
83 		size_t len = 0;
84 		uint8_t *cert_tmp;
85 
86 		err = tls_credential_get(certs[i].sec_tag, certs[i].type, NULL, &len);
87 		if (err != -EFBIG) {
88 			LOG_ERR("Failed to get credential tag: %d length, err: %d",
89 				certs[i].sec_tag, err);
90 			return err;
91 		}
92 
93 		cert_tmp = k_malloc(len);
94 		if (!cert_tmp) {
95 			LOG_ERR("Failed to allocate memory for credential tag: %d",
96 				certs[i].sec_tag);
97 			return -ENOMEM;
98 		}
99 
100 		err = tls_credential_get(certs[i].sec_tag, certs[i].type, cert_tmp, &len);
101 		if (err) {
102 			LOG_ERR("Failed to get credential tag: %d", certs[i].sec_tag);
103 			k_free(cert_tmp);
104 			return err;
105 		}
106 
107 		*certs[i].data = cert_tmp;
108 		*certs[i].len = len;
109 	}
110 
111 	return 0;
112 }
113 
set_enterprise_creds_params(bool is_ap)114 static void set_enterprise_creds_params(bool is_ap)
115 {
116 	struct wifi_cert_data certs_common[] = {
117 		{
118 			.type = TLS_CREDENTIAL_CA_CERTIFICATE,
119 			.sec_tag = WIFI_CERT_CA_SEC_TAG,
120 			.data = &enterprise_creds_params.ca_cert,
121 			.len = &enterprise_creds_params.ca_cert_len,
122 		},
123 	};
124 	struct wifi_cert_data certs_sta[] = {
125 		{
126 			.type = TLS_CREDENTIAL_PRIVATE_KEY,
127 			.sec_tag = WIFI_CERT_CLIENT_KEY_SEC_TAG,
128 			.data = &enterprise_creds_params.client_key,
129 			.len = &enterprise_creds_params.client_key_len,
130 		},
131 		{
132 			.type = TLS_CREDENTIAL_PUBLIC_CERTIFICATE,
133 			.sec_tag = WIFI_CERT_CLIENT_SEC_TAG,
134 			.data = &enterprise_creds_params.client_cert,
135 			.len = &enterprise_creds_params.client_cert_len,
136 		},
137 		{
138 			.type = TLS_CREDENTIAL_CA_CERTIFICATE,
139 			.sec_tag = WIFI_CERT_CA_P2_SEC_TAG,
140 			.data = &enterprise_creds_params.ca_cert2,
141 			.len = &enterprise_creds_params.ca_cert2_len,
142 		},
143 		{
144 			.type = TLS_CREDENTIAL_PRIVATE_KEY,
145 			.sec_tag = WIFI_CERT_CLIENT_KEY_P2_SEC_TAG,
146 			.data = &enterprise_creds_params.client_key2,
147 			.len = &enterprise_creds_params.client_key2_len,
148 		},
149 		{
150 			.type = TLS_CREDENTIAL_PUBLIC_CERTIFICATE,
151 			.sec_tag = WIFI_CERT_CLIENT_P2_SEC_TAG,
152 			.data = &enterprise_creds_params.client_cert2,
153 			.len = &enterprise_creds_params.client_cert2_len,
154 		},
155 	};
156 
157 	struct wifi_cert_data certs_ap[] = {
158 		{
159 			.type = TLS_CREDENTIAL_PUBLIC_CERTIFICATE,
160 			.sec_tag = WIFI_CERT_SERVER_SEC_TAG,
161 			.data = &enterprise_creds_params.server_cert,
162 			.len = &enterprise_creds_params.server_cert_len,
163 		},
164 		{
165 			.type = TLS_CREDENTIAL_PRIVATE_KEY,
166 			.sec_tag = WIFI_CERT_SERVER_KEY_SEC_TAG,
167 			.data = &enterprise_creds_params.server_key,
168 			.len = &enterprise_creds_params.server_key_len,
169 		},
170 	};
171 
172 	memset(&enterprise_creds_params, 0, sizeof(struct wifi_enterprise_creds_params));
173 
174 	/* Process common certificates */
175 	if (process_certificates(certs_common, ARRAY_SIZE(certs_common)) != 0) {
176 		goto cleanup;
177 	}
178 
179 	/* Process STA-specific certificates */
180 	if (!is_ap) {
181 		if (process_certificates(certs_sta, ARRAY_SIZE(certs_sta)) != 0) {
182 			goto cleanup;
183 		}
184 	}
185 
186 	/* Process AP-specific certificates if is_ap is true */
187 	if (is_ap) {
188 		if (process_certificates(certs_ap, ARRAY_SIZE(certs_ap)) != 0) {
189 			goto cleanup;
190 		}
191 	}
192 
193 	return;
194 
195 cleanup:
196 	for (size_t i = 0; i < ARRAY_SIZE(certs_common); i++) {
197 		if (certs_common[i].data) {
198 			k_free(*certs_common[i].data);
199 		}
200 	}
201 
202 	if (!is_ap) {
203 		for (size_t i = 0; i < ARRAY_SIZE(certs_sta); i++) {
204 			if (certs_sta[i].data) {
205 				k_free(*certs_sta[i].data);
206 			}
207 		}
208 	}
209 
210 	if (is_ap) {
211 		for (size_t i = 0; i < ARRAY_SIZE(certs_ap); i++) {
212 			if (certs_ap[i].data) {
213 				k_free(*certs_ap[i].data);
214 			}
215 		}
216 	}
217 }
218 
wifi_clear_enterprise_credentials(void)219 void wifi_clear_enterprise_credentials(void)
220 {
221 	size_t i;
222 
223 	const uint8_t *certs[] = {
224 		enterprise_creds_params.ca_cert,
225 		enterprise_creds_params.client_cert,
226 		enterprise_creds_params.client_key,
227 		enterprise_creds_params.server_cert,
228 		enterprise_creds_params.server_key,
229 		enterprise_creds_params.ca_cert2,
230 		enterprise_creds_params.client_cert2,
231 		enterprise_creds_params.client_key2,
232 	};
233 
234 	for (i = 0; i < ARRAY_SIZE(certs); i++) {
235 		k_free((void *)certs[i]);
236 	}
237 	memset(&enterprise_creds_params, 0, sizeof(struct wifi_enterprise_creds_params));
238 }
239 #else
config_process_blob(struct wpa_config * config,char * name,uint8_t * data,uint32_t data_len)240 int config_process_blob(struct wpa_config *config, char *name, uint8_t *data,
241 			uint32_t data_len)
242 {
243 	struct wpa_config_blob *blob;
244 
245 	if (!data || !data_len) {
246 		return -1;
247 	}
248 
249 	blob = os_zalloc(sizeof(*blob));
250 	if (blob == NULL) {
251 		return -1;
252 	}
253 
254 	blob->data = os_zalloc(data_len);
255 	if (blob->data == NULL) {
256 		os_free(blob);
257 		return -1;
258 	}
259 
260 	blob->name = os_strdup(name);
261 
262 	if (blob->name == NULL) {
263 		wpa_config_free_blob(blob);
264 		return -1;
265 	}
266 
267 	os_memcpy(blob->data, data, data_len);
268 	blob->len = data_len;
269 
270 	wpa_config_set_blob(config, blob);
271 
272 	return 0;
273 }
274 
process_certificates(void)275 int process_certificates(void)
276 {
277 	struct wpa_supplicant *wpa_s;
278 	int ret;
279 	char if_name[CONFIG_NET_INTERFACE_NAME_LEN + 1];
280 	struct net_if *iface = net_if_get_wifi_sta();
281 
282 	ret = net_if_get_name(iface, if_name, sizeof(if_name));
283 	if (!ret) {
284 		LOG_ERR("Cannot get interface name (%d)", ret);
285 		return -1;
286 	}
287 
288 	wpa_s = zephyr_get_handle_by_ifname(if_name);
289 	if (!wpa_s) {
290 		LOG_ERR("Unable to find the interface: %s, quitting", if_name);
291 		return -1;
292 	}
293 
294 	wifi_set_enterprise_credentials(iface, 0);
295 
296 	if (config_process_blob(wpa_s->conf, "ca_cert",
297 				enterprise_creds_params.ca_cert,
298 				enterprise_creds_params.ca_cert_len)) {
299 		return -1;
300 	}
301 
302 	if (config_process_blob(wpa_s->conf, "client_cert",
303 				enterprise_creds_params.client_cert,
304 				enterprise_creds_params.client_cert_len)) {
305 		return -1;
306 	}
307 
308 	if (config_process_blob(wpa_s->conf, "private_key",
309 				enterprise_creds_params.client_key,
310 				enterprise_creds_params.client_key_len)) {
311 		return -1;
312 	}
313 
314 	return 0;
315 }
316 
set_enterprise_creds_params(bool is_ap)317 static void set_enterprise_creds_params(bool is_ap)
318 {
319 	enterprise_creds_params.ca_cert = (uint8_t *)ca_cert_test;
320 	enterprise_creds_params.ca_cert_len = ARRAY_SIZE(ca_cert_test);
321 
322 	if (!is_ap) {
323 		enterprise_creds_params.client_cert = (uint8_t *)client_cert_test;
324 		enterprise_creds_params.client_cert_len = ARRAY_SIZE(client_cert_test);
325 		enterprise_creds_params.client_key = (uint8_t *)client_key_test;
326 		enterprise_creds_params.client_key_len = ARRAY_SIZE(client_key_test);
327 		enterprise_creds_params.ca_cert2 = (uint8_t *)ca_cert2_test;
328 		enterprise_creds_params.ca_cert2_len = ARRAY_SIZE(ca_cert2_test);
329 		enterprise_creds_params.client_cert2 = (uint8_t *)client_cert2_test;
330 		enterprise_creds_params.client_cert2_len = ARRAY_SIZE(client_cert2_test);
331 		enterprise_creds_params.client_key2 = (uint8_t *)client_key2_test;
332 		enterprise_creds_params.client_key2_len = ARRAY_SIZE(client_key2_test);
333 
334 		return;
335 	}
336 
337 	enterprise_creds_params.server_cert = (uint8_t *)server_cert_test;
338 	enterprise_creds_params.server_cert_len = ARRAY_SIZE(server_cert_test);
339 	enterprise_creds_params.server_key = (uint8_t *)server_key_test;
340 	enterprise_creds_params.server_key_len = ARRAY_SIZE(server_key_test);
341 }
342 
wifi_clear_enterprise_credentials(void)343 void wifi_clear_enterprise_credentials(void)
344 {
345 	/**
346 	 * No operation needed because Wi-Fi credentials
347 	 * are statically configured at build time and
348 	 * no dynamic memory needs to be freed.
349 	 */
350 }
351 #endif /* CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES */
352 
wifi_set_enterprise_credentials(struct net_if * iface,bool is_ap)353 int wifi_set_enterprise_credentials(struct net_if *iface, bool is_ap)
354 {
355 #ifdef CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES
356 	wifi_clear_enterprise_credentials();
357 #endif /* CONFIG_WIFI_SHELL_RUNTIME_CERTIFICATES */
358 	set_enterprise_creds_params(is_ap);
359 	if (net_mgmt(NET_REQUEST_WIFI_ENTERPRISE_CREDS, iface,
360 			&enterprise_creds_params, sizeof(enterprise_creds_params))) {
361 		LOG_WRN("Set enterprise credentials failed\n");
362 		return -1;
363 	}
364 
365 	return 0;
366 }
367