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