1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/init.h>
10 #include <zephyr/net/wifi_credentials.h>
11 #include <sys/types.h>
12
13 #include "wifi_credentials_internal.h"
14
15 LOG_MODULE_REGISTER(wifi_credentials, CONFIG_WIFI_CREDENTIALS_LOG_LEVEL);
16
17 static K_MUTEX_DEFINE(wifi_credentials_mutex);
18
19 /* SSID cache: maps SSIDs to their storage indices */
20 static char ssid_cache[CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES][WIFI_SSID_MAX_LEN];
21 static size_t ssid_cache_lengths[CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES];
22
23 /**
24 * @brief Finds index of given SSID if it exists.
25 *
26 * @param ssid SSID to look for (buffer of WIFI_SSID_MAX_LEN length)
27 * @return index if entry is found, -1 otherwise
28 */
lookup_idx(const uint8_t * ssid,size_t ssid_len)29 static inline ssize_t lookup_idx(const uint8_t *ssid, size_t ssid_len)
30 {
31 for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
32 if (ssid_len != ssid_cache_lengths[i]) {
33 continue;
34 }
35
36 if (strncmp(ssid, ssid_cache[i], ssid_len) == 0) {
37 return i;
38 }
39 }
40
41 return -1;
42 }
43
44 /**
45 * @brief Determine whether an index is currently used for storing network credentials.
46 *
47 * @param idx credential index
48 * @return true if index is used, false otherwise
49 */
is_entry_used(size_t idx)50 static inline bool is_entry_used(size_t idx)
51 {
52 return ssid_cache_lengths[idx] != 0;
53 }
54
55 /**
56 * @brief Finds unused index to store new entry at.
57 *
58 * @return index if empty slot is found, -1 otherwise
59 */
lookup_unused_idx(void)60 static inline ssize_t lookup_unused_idx(void)
61 {
62 for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
63 if (!is_entry_used(i)) {
64 return i;
65 }
66 }
67
68 return -1;
69 }
70
init(void)71 static int init(void)
72 {
73
74 int ret;
75
76 k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
77
78 ret = wifi_credentials_backend_init();
79 if (ret) {
80 LOG_ERR("Initializing WiFi credentials storage backend failed, err: %d", ret);
81 }
82
83 k_mutex_unlock(&wifi_credentials_mutex);
84
85 return 0;
86 }
87
wifi_credentials_cache_ssid(size_t idx,const struct wifi_credentials_header * buf)88 void wifi_credentials_cache_ssid(size_t idx, const struct wifi_credentials_header *buf)
89 {
90 memcpy(ssid_cache[idx], buf->ssid, buf->ssid_len);
91 ssid_cache_lengths[idx] = buf->ssid_len;
92 }
93
94 /**
95 * @brief Clear entry in SSID cache.
96 *
97 * @param idx credential index
98 */
wifi_credentials_uncache_ssid(size_t idx)99 void wifi_credentials_uncache_ssid(size_t idx)
100 {
101 ssid_cache_lengths[idx] = 0;
102 }
103
wifi_credentials_get_by_ssid_personal_struct(const char * ssid,size_t ssid_len,struct wifi_credentials_personal * buf)104 int wifi_credentials_get_by_ssid_personal_struct(const char *ssid, size_t ssid_len,
105 struct wifi_credentials_personal *buf)
106 {
107 ssize_t idx;
108 int ret;
109
110 if (ssid == NULL || ssid_len > WIFI_SSID_MAX_LEN || ssid_len == 0) {
111 LOG_ERR("Cannot %s WiFi credentials, %s", "retrieve", "SSID has invalid format");
112 return -EINVAL;
113 }
114
115 if (buf == NULL) {
116 LOG_ERR("Cannot %s WiFi credentials, %s", "retrieve", "destination is NULL");
117 return -EINVAL;
118 }
119
120 k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
121
122 idx = lookup_idx(ssid, ssid_len);
123 if (idx == -1) {
124 LOG_DBG("Cannot %s WiFi credentials, %s", "retrieve", "SSID not found");
125 ret = -ENOENT;
126 goto exit;
127 }
128
129 ret = wifi_credentials_load_entry(idx, buf, sizeof(struct wifi_credentials_personal));
130 if (ret) {
131 LOG_ERR("Failed to %s WiFi credentials at index %d, err: %d", "load", idx, ret);
132 goto exit;
133 }
134
135 if (buf->header.type != WIFI_SECURITY_TYPE_NONE &&
136 buf->header.type != WIFI_SECURITY_TYPE_PSK &&
137 buf->header.type != WIFI_SECURITY_TYPE_PSK_SHA256 &&
138 buf->header.type != WIFI_SECURITY_TYPE_SAE &&
139 buf->header.type != WIFI_SECURITY_TYPE_WPA_PSK) {
140 LOG_ERR("Requested WiFi credentials entry is corrupted");
141 ret = -EPROTO;
142 goto exit;
143 }
144
145 exit:
146 k_mutex_unlock(&wifi_credentials_mutex);
147
148 return ret;
149 }
150
wifi_credentials_set_personal_struct(const struct wifi_credentials_personal * creds)151 int wifi_credentials_set_personal_struct(const struct wifi_credentials_personal *creds)
152 {
153 ssize_t idx;
154 int ret;
155
156 if (creds == NULL) {
157 LOG_ERR("Cannot %s WiFi credentials, %s", "set", "credential not set");
158 return -EINVAL;
159 }
160
161 if (creds->header.ssid_len > WIFI_SSID_MAX_LEN || creds->header.ssid_len == 0) {
162 LOG_ERR("Cannot %s WiFi credentials, %s", "set", "SSID has invalid format");
163 return -EINVAL;
164 }
165
166 k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
167
168 idx = lookup_idx(creds->header.ssid, creds->header.ssid_len);
169 if (idx == -1) {
170 idx = lookup_unused_idx();
171 if (idx == -1) {
172 LOG_ERR("Cannot %s WiFi credentials, %s", "store", "no space left");
173 ret = -ENOBUFS;
174 goto exit;
175 }
176 }
177
178 ret = wifi_credentials_store_entry(idx, creds, sizeof(struct wifi_credentials_personal));
179 if (ret) {
180 LOG_ERR("Failed to %s WiFi credentials at index %d, err: %d", "store", idx, ret);
181 goto exit;
182 }
183
184 wifi_credentials_cache_ssid(idx, &creds->header);
185
186 exit:
187 k_mutex_unlock(&wifi_credentials_mutex);
188
189 return ret;
190 }
191
wifi_credentials_set_personal(const char * ssid,size_t ssid_len,enum wifi_security_type type,const uint8_t * bssid,size_t bssid_len,const char * password,size_t password_len,uint32_t flags,uint8_t channel,uint32_t timeout)192 int wifi_credentials_set_personal(const char *ssid, size_t ssid_len, enum wifi_security_type type,
193 const uint8_t *bssid, size_t bssid_len, const char *password,
194 size_t password_len, uint32_t flags, uint8_t channel,
195 uint32_t timeout)
196 {
197 int ret = 0;
198 uint8_t buf[ENTRY_MAX_LEN] = {0};
199 struct wifi_credentials_header *header;
200
201 if (ssid == NULL || ssid_len > WIFI_SSID_MAX_LEN || ssid_len == 0) {
202 LOG_ERR("Cannot %s WiFi credentials, %s", "set", "SSID has invalid format");
203 return -EINVAL;
204 }
205
206 if (flags & WIFI_CREDENTIALS_FLAG_BSSID &&
207 (bssid_len != WIFI_MAC_ADDR_LEN || bssid == NULL)) {
208 LOG_ERR("Cannot %s WiFi credentials, %s", "set",
209 "provided flags indicated BSSID but no BSSID provided");
210 return -EINVAL;
211 }
212
213 if ((type != WIFI_SECURITY_TYPE_NONE && (password_len == 0 || password == NULL)) ||
214 (password_len > WIFI_CREDENTIALS_MAX_PASSWORD_LEN)) {
215 LOG_ERR("Cannot %s WiFi credentials, %s", "set",
216 "password not provided or invalid");
217 return -EINVAL;
218 }
219
220 /* pack entry */
221 header = (struct wifi_credentials_header *)buf;
222
223 header->type = type;
224 memcpy(header->ssid, ssid, ssid_len);
225 header->ssid_len = ssid_len;
226 header->flags = flags;
227 header->channel = channel;
228 header->timeout = timeout;
229
230 if (flags & WIFI_CREDENTIALS_FLAG_BSSID) {
231 memcpy(header->bssid, bssid, WIFI_MAC_ADDR_LEN);
232 }
233
234 switch (type) {
235 case WIFI_SECURITY_TYPE_NONE:
236 break;
237 case WIFI_SECURITY_TYPE_PSK:
238 case WIFI_SECURITY_TYPE_PSK_SHA256:
239 case WIFI_SECURITY_TYPE_WPA_PSK:
240 case WIFI_SECURITY_TYPE_SAE: {
241 struct wifi_credentials_personal *header_personal =
242 (struct wifi_credentials_personal *)buf;
243
244 memcpy(header_personal->password, password, password_len);
245 header_personal->password_len = password_len;
246 break;
247 }
248 default:
249 LOG_ERR("Cannot %s WiFi credentials, provided security type %d is unsupported",
250 "set", type);
251 return -ENOTSUP;
252 }
253
254 /* store entry */
255 ret = wifi_credentials_set_personal_struct((struct wifi_credentials_personal *)buf);
256
257 return ret;
258 }
259
wifi_credentials_get_by_ssid_personal(const char * ssid,size_t ssid_len,enum wifi_security_type * type,uint8_t * bssid_buf,size_t bssid_buf_len,char * password_buf,size_t password_buf_len,size_t * password_len,uint32_t * flags,uint8_t * channel,uint32_t * timeout)260 int wifi_credentials_get_by_ssid_personal(const char *ssid, size_t ssid_len,
261 enum wifi_security_type *type, uint8_t *bssid_buf,
262 size_t bssid_buf_len, char *password_buf,
263 size_t password_buf_len, size_t *password_len,
264 uint32_t *flags, uint8_t *channel, uint32_t *timeout)
265 {
266 int ret = 0;
267 uint8_t buf[ENTRY_MAX_LEN] = {0};
268 struct wifi_credentials_header *header;
269
270 if (ssid == NULL || ssid_len > WIFI_SSID_MAX_LEN || ssid_len == 0) {
271 LOG_ERR("Cannot %s WiFi credentials, %s", "retrieve", "SSID has invalid format");
272 return -EINVAL;
273 }
274
275 if (bssid_buf_len != WIFI_MAC_ADDR_LEN || bssid_buf == NULL) {
276 LOG_ERR("%s buffer needs to be provided", "BSSID");
277 return -EINVAL;
278 }
279
280 if (password_buf == NULL || password_buf_len > WIFI_CREDENTIALS_MAX_PASSWORD_LEN ||
281 password_buf_len == 0) {
282 LOG_ERR("%s buffer needs to be provided", "WiFi password");
283 return -EINVAL;
284 }
285
286 /* load entry */
287 ret = wifi_credentials_get_by_ssid_personal_struct(ssid, ssid_len,
288 (struct wifi_credentials_personal *)buf);
289 if (ret) {
290 return ret;
291 }
292
293 /* unpack entry*/
294 header = (struct wifi_credentials_header *)buf;
295
296 *type = header->type;
297 *flags = header->flags;
298 *channel = header->channel;
299 *timeout = header->timeout;
300
301 if (header->flags & WIFI_CREDENTIALS_FLAG_BSSID) {
302 memcpy(bssid_buf, header->bssid, WIFI_MAC_ADDR_LEN);
303 }
304
305 switch (header->type) {
306 case WIFI_SECURITY_TYPE_NONE:
307 break;
308 case WIFI_SECURITY_TYPE_PSK:
309 case WIFI_SECURITY_TYPE_PSK_SHA256:
310 case WIFI_SECURITY_TYPE_WPA_PSK:
311 case WIFI_SECURITY_TYPE_SAE: {
312 struct wifi_credentials_personal *header_personal =
313 (struct wifi_credentials_personal *)buf;
314
315 memcpy(password_buf, header_personal->password, header_personal->password_len);
316 *password_len = header_personal->password_len;
317 break;
318 }
319 default:
320 LOG_ERR("Cannot %s WiFi credentials, %s", "get",
321 "the requested credentials have invalid WIFI_SECURITY_TYPE");
322 ret = -EPROTO;
323 }
324 return ret;
325 }
326
wifi_credentials_delete_by_ssid(const char * ssid,size_t ssid_len)327 int wifi_credentials_delete_by_ssid(const char *ssid, size_t ssid_len)
328 {
329 ssize_t idx;
330 int ret = 0;
331
332 if (ssid == NULL || ssid_len > WIFI_SSID_MAX_LEN || ssid_len == 0) {
333 LOG_ERR("Cannot %s WiFi credentials, %s", "delete", "SSID has invalid format");
334 return -EINVAL;
335 }
336
337 k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
338
339 idx = lookup_idx(ssid, ssid_len);
340 if (idx == -1) {
341 LOG_DBG("WiFi credentials entry was not found");
342 goto exit;
343 }
344
345 ret = wifi_credentials_delete_entry(idx);
346 if (ret) {
347 LOG_ERR("Failed to %s WiFi credentials index %d, err: %d", "delete", idx, ret);
348 goto exit;
349 }
350
351 wifi_credentials_uncache_ssid(idx);
352
353 exit:
354 k_mutex_unlock(&wifi_credentials_mutex);
355 return ret;
356 }
357
wifi_credentials_for_each_ssid(wifi_credentials_ssid_cb cb,void * cb_arg)358 void wifi_credentials_for_each_ssid(wifi_credentials_ssid_cb cb, void *cb_arg)
359 {
360 k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
361
362 for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
363 if (is_entry_used(i)) {
364 cb(cb_arg, ssid_cache[i], ssid_cache_lengths[i]);
365 }
366 }
367
368 k_mutex_unlock(&wifi_credentials_mutex);
369 }
370
wifi_credentials_is_empty(void)371 bool wifi_credentials_is_empty(void)
372 {
373 k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
374
375 for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
376 if (is_entry_used(i)) {
377 k_mutex_unlock(&wifi_credentials_mutex);
378 return false;
379 }
380 }
381
382 k_mutex_unlock(&wifi_credentials_mutex);
383 return true;
384 }
385
wifi_credentials_delete_all(void)386 int wifi_credentials_delete_all(void)
387 {
388 int ret = 0;
389
390 k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
391 for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
392 if (is_entry_used(i)) {
393 ret = wifi_credentials_delete_entry(i);
394 if (ret) {
395 LOG_ERR("Failed to %s WiFi credentials index %d, err: %d",
396 "delete", i, ret);
397 break;
398 }
399
400 wifi_credentials_uncache_ssid(i);
401 }
402 }
403
404 k_mutex_unlock(&wifi_credentials_mutex);
405 return ret;
406 }
407
408 SYS_INIT(init, POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY);
409