1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/logging/log.h>
9 #include <stdlib.h>
10 #include <zephyr/settings/settings.h>
11 
12 #include "wifi_credentials_internal.h"
13 
14 LOG_MODULE_REGISTER(wifi_credentials_backend, CONFIG_WIFI_CREDENTIALS_LOG_LEVEL);
15 
16 BUILD_ASSERT(ENTRY_MAX_LEN <= SETTINGS_MAX_VAL_LEN);
17 
18 #define WIFI_CREDENTIALS_SBE_BASE_KEY "wifi_cred"
19 #define WIFI_CREDENTIALS_SBE_KEY_SIZE                                                              \
20 	sizeof(WIFI_CREDENTIALS_SBE_BASE_KEY "/" STRINGIFY(CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES))
21 #define WIFI_CREDENTIALS_SBE_KEY_FMT WIFI_CREDENTIALS_SBE_BASE_KEY "/%d"
22 
23 /* Type of the callback argument used in the function below. */
24 struct zephyr_settings_backend_load_cb_arg {
25 	uint8_t *buf;
26 	size_t buf_len;
27 	size_t idx;
28 	bool found;
29 };
30 
31 /* This callback function is used to retrieve credentials on demand. */
zephyr_settings_backend_load_val_cb(const char * key,size_t len,settings_read_cb read_cb,void * cb_arg,void * param)32 static int zephyr_settings_backend_load_val_cb(const char *key, size_t len,
33 					       settings_read_cb read_cb, void *cb_arg, void *param)
34 {
35 	struct zephyr_settings_backend_load_cb_arg *arg = param;
36 	int idx = atoi(key);
37 
38 	if (arg->idx != idx) {
39 		LOG_DBG("Skipping index [%s]", key);
40 		return 0;
41 	}
42 
43 	if (len != arg->buf_len) {
44 		LOG_ERR("Settings error: invalid settings length");
45 		return -EINVAL;
46 	}
47 
48 	size_t length_read = read_cb(cb_arg, arg->buf, arg->buf_len);
49 
50 	/* value validation */
51 	if (length_read < len) {
52 		LOG_ERR("Settings error: entry incomplete");
53 		return -ENODATA;
54 	}
55 
56 	arg->found = true;
57 
58 	return 0;
59 }
60 
61 /* This callback function is used to initialize the SSID cache. */
zephyr_settings_backend_load_key_cb(const char * key,size_t len,settings_read_cb read_cb,void * cb_arg,void * param)62 static int zephyr_settings_backend_load_key_cb(const char *key, size_t len,
63 					       settings_read_cb read_cb, void *cb_arg, void *param)
64 {
65 	ARG_UNUSED(param);
66 
67 	/* key validation */
68 	if (!key) {
69 		LOG_ERR("Settings error: no key");
70 		return -EINVAL;
71 	}
72 
73 	int idx = atoi(key);
74 
75 	if ((idx == 0 && strcmp(key, "0") != 0) || idx >= CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES) {
76 		LOG_ERR("Settings error: index too large");
77 		return -EINVAL;
78 	}
79 
80 	if (len < sizeof(struct wifi_credentials_header)) {
81 		LOG_ERR("Settings error: invalid settings length");
82 		return -EINVAL;
83 	}
84 
85 	uint8_t buf[ENTRY_MAX_LEN];
86 	size_t length_read = read_cb(cb_arg, buf, ARRAY_SIZE(buf));
87 
88 	/* value validation */
89 	if (length_read < len) {
90 		LOG_ERR("Settings error: entry incomplete");
91 		return -ENODATA;
92 	}
93 
94 	wifi_credentials_cache_ssid(idx, (struct wifi_credentials_header *)buf);
95 	return 0;
96 }
97 
wifi_credentials_backend_init(void)98 int wifi_credentials_backend_init(void)
99 {
100 	int ret = settings_subsys_init();
101 
102 	if (ret) {
103 		LOG_ERR("Initializing settings subsystem failed: %d", ret);
104 		return ret;
105 	}
106 
107 	ret = settings_load_subtree_direct(WIFI_CREDENTIALS_SBE_BASE_KEY,
108 					   zephyr_settings_backend_load_key_cb, NULL);
109 
110 	return ret;
111 }
112 
wifi_credentials_store_entry(size_t idx,const void * buf,size_t buf_len)113 int wifi_credentials_store_entry(size_t idx, const void *buf, size_t buf_len)
114 {
115 	char settings_name_buf[WIFI_CREDENTIALS_SBE_KEY_SIZE] = {0};
116 
117 	int ret = snprintk(settings_name_buf, ARRAY_SIZE(settings_name_buf),
118 			   WIFI_CREDENTIALS_SBE_KEY_FMT, idx);
119 
120 	if (ret < 0 || ret == ARRAY_SIZE(settings_name_buf)) {
121 		LOG_ERR("WiFi credentials settings key could not be generated, idx: %d", idx);
122 		return -EFAULT;
123 	}
124 
125 	return settings_save_one(settings_name_buf, buf, buf_len);
126 }
127 
wifi_credentials_delete_entry(size_t idx)128 int wifi_credentials_delete_entry(size_t idx)
129 {
130 	char settings_name_buf[WIFI_CREDENTIALS_SBE_KEY_SIZE] = {0};
131 
132 	int ret = snprintk(settings_name_buf, ARRAY_SIZE(settings_name_buf),
133 			   WIFI_CREDENTIALS_SBE_KEY_FMT, idx);
134 
135 	if (ret < 0 || ret == ARRAY_SIZE(settings_name_buf)) {
136 		LOG_ERR("WiFi credentials settings key could not be generated, idx: %d", idx);
137 		return -EFAULT;
138 	}
139 
140 	return settings_delete(settings_name_buf);
141 }
142 
wifi_credentials_load_entry(size_t idx,void * buf,size_t buf_len)143 int wifi_credentials_load_entry(size_t idx, void *buf, size_t buf_len)
144 {
145 	struct zephyr_settings_backend_load_cb_arg arg = {
146 		.buf = buf,
147 		.buf_len = buf_len,
148 		.idx = idx,
149 		.found = false,
150 	};
151 	int ret;
152 
153 	/* Browse through the settings entries with custom callback to load the whole entry. */
154 	ret = settings_load_subtree_direct(WIFI_CREDENTIALS_SBE_BASE_KEY,
155 					   zephyr_settings_backend_load_val_cb, &arg);
156 
157 	if (ret) {
158 		return ret;
159 	}
160 
161 	if (!arg.found) {
162 		return -ENOENT;
163 	}
164 
165 	return 0;
166 }
167