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 < 0 || buf->header.type > WIFI_SECURITY_TYPE_MAX) {
136 LOG_ERR("Requested WiFi credentials entry is corrupted");
137 ret = -EPROTO;
138 goto exit;
139 }
140
141 exit:
142 k_mutex_unlock(&wifi_credentials_mutex);
143
144 return ret;
145 }
146
wifi_credentials_set_personal_struct(const struct wifi_credentials_personal * creds)147 int wifi_credentials_set_personal_struct(const struct wifi_credentials_personal *creds)
148 {
149 ssize_t idx;
150 int ret;
151
152 if (creds == NULL) {
153 LOG_ERR("Cannot %s WiFi credentials, %s", "set", "credential not set");
154 return -EINVAL;
155 }
156
157 if (creds->header.ssid_len > WIFI_SSID_MAX_LEN || creds->header.ssid_len == 0) {
158 LOG_ERR("Cannot %s WiFi credentials, %s", "set", "SSID has invalid format");
159 return -EINVAL;
160 }
161
162 k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
163
164 idx = lookup_idx(creds->header.ssid, creds->header.ssid_len);
165 if (idx == -1) {
166 idx = lookup_unused_idx();
167 if (idx == -1) {
168 LOG_ERR("Cannot %s WiFi credentials, %s", "store", "no space left");
169 ret = -ENOBUFS;
170 goto exit;
171 }
172 }
173
174 ret = wifi_credentials_store_entry(idx, creds, sizeof(struct wifi_credentials_personal));
175 if (ret) {
176 LOG_ERR("Failed to %s WiFi credentials at index %d, err: %d", "store", idx, ret);
177 goto exit;
178 }
179
180 wifi_credentials_cache_ssid(idx, &creds->header);
181
182 exit:
183 k_mutex_unlock(&wifi_credentials_mutex);
184
185 return ret;
186 }
187
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)188 int wifi_credentials_set_personal(const char *ssid, size_t ssid_len, enum wifi_security_type type,
189 const uint8_t *bssid, size_t bssid_len, const char *password,
190 size_t password_len, uint32_t flags, uint8_t channel,
191 uint32_t timeout)
192 {
193 int ret = 0;
194 uint8_t buf[ENTRY_MAX_LEN] = {0};
195 struct wifi_credentials_header *header;
196
197 if (ssid == NULL || ssid_len > WIFI_SSID_MAX_LEN || ssid_len == 0) {
198 LOG_ERR("Cannot %s WiFi credentials, %s", "set", "SSID has invalid format");
199 return -EINVAL;
200 }
201
202 if (flags & WIFI_CREDENTIALS_FLAG_BSSID &&
203 (bssid_len != WIFI_MAC_ADDR_LEN || bssid == NULL)) {
204 LOG_ERR("Cannot %s WiFi credentials, %s", "set",
205 "provided flags indicated BSSID but no BSSID provided");
206 return -EINVAL;
207 }
208
209 if ((type != WIFI_SECURITY_TYPE_NONE && (password_len == 0 || password == NULL)) ||
210 (password_len > WIFI_CREDENTIALS_MAX_PASSWORD_LEN)) {
211 LOG_ERR("Cannot %s WiFi credentials, %s", "set",
212 "password not provided or invalid");
213 return -EINVAL;
214 }
215
216 /* pack entry */
217 header = (struct wifi_credentials_header *)buf;
218
219 header->type = type;
220 memcpy(header->ssid, ssid, ssid_len);
221 header->ssid_len = ssid_len;
222 header->flags = flags;
223 header->channel = channel;
224 header->timeout = timeout;
225
226 if (flags & WIFI_CREDENTIALS_FLAG_BSSID) {
227 memcpy(header->bssid, bssid, WIFI_MAC_ADDR_LEN);
228 }
229
230 switch (type) {
231 case WIFI_SECURITY_TYPE_NONE:
232 break;
233 case WIFI_SECURITY_TYPE_PSK:
234 case WIFI_SECURITY_TYPE_PSK_SHA256:
235 case WIFI_SECURITY_TYPE_WPA_PSK:
236 case WIFI_SECURITY_TYPE_SAE: {
237 struct wifi_credentials_personal *header_personal =
238 (struct wifi_credentials_personal *)buf;
239
240 memcpy(header_personal->password, password, password_len);
241 header_personal->password_len = password_len;
242 break;
243 }
244 default:
245 LOG_ERR("Cannot %s WiFi credentials, provided security type %d is unsupported",
246 "set", type);
247 return -ENOTSUP;
248 }
249
250 /* store entry */
251 ret = wifi_credentials_set_personal_struct((struct wifi_credentials_personal *)buf);
252
253 return ret;
254 }
255
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)256 int wifi_credentials_get_by_ssid_personal(const char *ssid, size_t ssid_len,
257 enum wifi_security_type *type, uint8_t *bssid_buf,
258 size_t bssid_buf_len, char *password_buf,
259 size_t password_buf_len, size_t *password_len,
260 uint32_t *flags, uint8_t *channel, uint32_t *timeout)
261 {
262 int ret = 0;
263 uint8_t buf[ENTRY_MAX_LEN] = {0};
264 struct wifi_credentials_header *header;
265
266 if (ssid == NULL || ssid_len > WIFI_SSID_MAX_LEN || ssid_len == 0) {
267 LOG_ERR("Cannot %s WiFi credentials, %s", "retrieve", "SSID has invalid format");
268 return -EINVAL;
269 }
270
271 if (bssid_buf_len != WIFI_MAC_ADDR_LEN || bssid_buf == NULL) {
272 LOG_ERR("%s buffer needs to be provided", "BSSID");
273 return -EINVAL;
274 }
275
276 if (password_buf == NULL || password_buf_len > WIFI_CREDENTIALS_MAX_PASSWORD_LEN ||
277 password_buf_len == 0) {
278 LOG_ERR("%s buffer needs to be provided", "WiFi password");
279 return -EINVAL;
280 }
281
282 /* load entry */
283 ret = wifi_credentials_get_by_ssid_personal_struct(ssid, ssid_len,
284 (struct wifi_credentials_personal *)buf);
285 if (ret) {
286 return ret;
287 }
288
289 /* unpack entry*/
290 header = (struct wifi_credentials_header *)buf;
291
292 *type = header->type;
293 *flags = header->flags;
294 *channel = header->channel;
295 *timeout = header->timeout;
296
297 if (header->flags & WIFI_CREDENTIALS_FLAG_BSSID) {
298 memcpy(bssid_buf, header->bssid, WIFI_MAC_ADDR_LEN);
299 }
300
301 switch (header->type) {
302 case WIFI_SECURITY_TYPE_NONE:
303 break;
304 case WIFI_SECURITY_TYPE_PSK:
305 case WIFI_SECURITY_TYPE_PSK_SHA256:
306 case WIFI_SECURITY_TYPE_WPA_PSK:
307 case WIFI_SECURITY_TYPE_SAE: {
308 struct wifi_credentials_personal *header_personal =
309 (struct wifi_credentials_personal *)buf;
310
311 memcpy(password_buf, header_personal->password, header_personal->password_len);
312 *password_len = header_personal->password_len;
313 break;
314 }
315 default:
316 LOG_ERR("Cannot %s WiFi credentials, %s", "get",
317 "the requested credentials have invalid WIFI_SECURITY_TYPE");
318 ret = -EPROTO;
319 }
320 return ret;
321 }
322
wifi_credentials_delete_by_ssid(const char * ssid,size_t ssid_len)323 int wifi_credentials_delete_by_ssid(const char *ssid, size_t ssid_len)
324 {
325 ssize_t idx;
326 int ret = 0;
327
328 if (ssid == NULL || ssid_len > WIFI_SSID_MAX_LEN || ssid_len == 0) {
329 LOG_ERR("Cannot %s WiFi credentials, %s", "delete", "SSID has invalid format");
330 return -EINVAL;
331 }
332
333 k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
334
335 idx = lookup_idx(ssid, ssid_len);
336 if (idx == -1) {
337 LOG_DBG("WiFi credentials entry was not found");
338 goto exit;
339 }
340
341 ret = wifi_credentials_delete_entry(idx);
342 if (ret) {
343 LOG_ERR("Failed to %s WiFi credentials index %d, err: %d", "delete", idx, ret);
344 goto exit;
345 }
346
347 wifi_credentials_uncache_ssid(idx);
348
349 exit:
350 k_mutex_unlock(&wifi_credentials_mutex);
351 return ret;
352 }
353
wifi_credentials_for_each_ssid(wifi_credentials_ssid_cb cb,void * cb_arg)354 void wifi_credentials_for_each_ssid(wifi_credentials_ssid_cb cb, void *cb_arg)
355 {
356 k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
357
358 for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
359 if (is_entry_used(i)) {
360 cb(cb_arg, ssid_cache[i], ssid_cache_lengths[i]);
361 }
362 }
363
364 k_mutex_unlock(&wifi_credentials_mutex);
365 }
366
wifi_credentials_is_empty(void)367 bool wifi_credentials_is_empty(void)
368 {
369 k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
370
371 for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
372 if (is_entry_used(i)) {
373 k_mutex_unlock(&wifi_credentials_mutex);
374 return false;
375 }
376 }
377
378 k_mutex_unlock(&wifi_credentials_mutex);
379 return true;
380 }
381
wifi_credentials_delete_all(void)382 int wifi_credentials_delete_all(void)
383 {
384 int ret = 0;
385
386 k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
387 for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
388 if (is_entry_used(i)) {
389 ret = wifi_credentials_delete_entry(i);
390 if (ret) {
391 LOG_ERR("Failed to %s WiFi credentials index %d, err: %d",
392 "delete", i, ret);
393 break;
394 }
395
396 wifi_credentials_uncache_ssid(i);
397 }
398 }
399
400 k_mutex_unlock(&wifi_credentials_mutex);
401 return ret;
402 }
403
404 SYS_INIT(init, POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY);
405