1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdbool.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/ztest.h>
12 
13 #include <zephyr/fff.h>
14 
15 #include <zephyr/net/wifi_credentials.h>
16 
17 #include "wifi_credentials_internal.h"
18 
19 DEFINE_FFF_GLOBALS;
20 
21 FAKE_VALUE_FUNC(int, wifi_credentials_store_entry, size_t, const void *, size_t)
22 FAKE_VALUE_FUNC(int, wifi_credentials_load_entry, size_t, void *, size_t)
23 FAKE_VALUE_FUNC(int, wifi_credentials_delete_entry, size_t)
24 FAKE_VALUE_FUNC(int, wifi_credentials_backend_init)
25 
26 uint8_t fake_settings_buf[CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES][ENTRY_MAX_LEN];
27 
custom_wifi_credentials_store_entry(size_t idx,const void * buf,size_t buf_len)28 int custom_wifi_credentials_store_entry(size_t idx, const void *buf, size_t buf_len)
29 {
30 	zassert_true(idx < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES, "Index out of bounds");
31 	memcpy(fake_settings_buf[idx], buf, MIN(ENTRY_MAX_LEN, buf_len));
32 	return 0;
33 }
34 
custom_wifi_credentials_load_entry(size_t idx,void * buf,size_t buf_len)35 int custom_wifi_credentials_load_entry(size_t idx, void *buf, size_t buf_len)
36 {
37 	zassert_true(idx < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES, "Index out of bounds");
38 	memcpy(buf, fake_settings_buf[idx], MIN(ENTRY_MAX_LEN, buf_len));
39 	return 0;
40 }
41 
42 #define SSID1     "test1"
43 #define PSK1      "super secret"
44 #define SECURITY1 WIFI_SECURITY_TYPE_PSK
45 #define BSSID1    "abcdef"
46 #define FLAGS1    WIFI_CREDENTIALS_FLAG_BSSID
47 #define CHANNEL1  1
48 
49 #define SSID2     "test2"
50 #define SECURITY2 WIFI_SECURITY_TYPE_NONE
51 #define FLAGS2    0
52 #define CHANNEL2  2
53 
54 #define SSID3     "test3"
55 #define PSK3      "extremely secret"
56 #define SECURITY3 WIFI_SECURITY_TYPE_SAE
57 #define FLAGS3    0
58 #define CHANNEL3  3
59 
60 #define SSID4     "\0what's\0null\0termination\0anyway"
61 #define PSK4      PSK1
62 #define SECURITY4 SECURITY1
63 #define BSSID4    BSSID1
64 #define FLAGS4    FLAGS1
65 #define CHANNEL4  4
66 
wifi_credentials_setup(void * unused)67 static void wifi_credentials_setup(void *unused)
68 {
69 	RESET_FAKE(wifi_credentials_store_entry);
70 	RESET_FAKE(wifi_credentials_load_entry);
71 	RESET_FAKE(wifi_credentials_delete_entry);
72 	wifi_credentials_store_entry_fake.custom_fake = custom_wifi_credentials_store_entry;
73 	wifi_credentials_load_entry_fake.custom_fake = custom_wifi_credentials_load_entry;
74 }
75 
wifi_credentials_teardown(void * unused)76 static void wifi_credentials_teardown(void *unused)
77 {
78 	wifi_credentials_delete_by_ssid(SSID1, ARRAY_SIZE(SSID1));
79 	wifi_credentials_delete_by_ssid(SSID2, ARRAY_SIZE(SSID2));
80 	wifi_credentials_delete_by_ssid(SSID3, ARRAY_SIZE(SSID3));
81 	wifi_credentials_delete_by_ssid(SSID4, ARRAY_SIZE(SSID4));
82 	wifi_credentials_delete_by_ssid("", 0);
83 }
84 
85 /* Verify that attempting to retrieve a non-existent credentials entry raises -ENOENT. */
ZTEST(wifi_credentials,test_get_non_existing)86 ZTEST(wifi_credentials, test_get_non_existing)
87 {
88 	int err;
89 	enum wifi_security_type security = -1;
90 	uint8_t bssid_buf[WIFI_MAC_ADDR_LEN] = "";
91 	char psk_buf[WIFI_CREDENTIALS_MAX_PASSWORD_LEN] = "";
92 	size_t psk_len = 0;
93 	uint32_t flags = 0;
94 	uint8_t channel = 0;
95 	uint32_t timeout = 0;
96 
97 	err = wifi_credentials_get_by_ssid_personal(
98 		SSID1, sizeof(SSID1), &security, bssid_buf, ARRAY_SIZE(bssid_buf), psk_buf,
99 		ARRAY_SIZE(psk_buf), &psk_len, &flags, &channel, &timeout);
100 	zassert_equal(err, -ENOENT, "Expected -ENOENT, got %d", err);
101 }
102 
103 /* Verify that we can successfully set/get a network without a specified BSSID. */
ZTEST(wifi_credentials,test_single_no_bssid)104 ZTEST(wifi_credentials, test_single_no_bssid)
105 {
106 	int err;
107 
108 	/* set network credentials without BSSID */
109 	err = wifi_credentials_set_personal(SSID1, sizeof(SSID1), SECURITY1, NULL, 0, PSK1,
110 					    sizeof(PSK1), 0, 0, 0);
111 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
112 
113 	enum wifi_security_type security = -1;
114 	uint8_t bssid_buf[WIFI_MAC_ADDR_LEN] = "";
115 	char psk_buf[WIFI_CREDENTIALS_MAX_PASSWORD_LEN] = "";
116 	size_t psk_len = 0;
117 	uint32_t flags = 0;
118 	uint8_t channel = 0;
119 	uint32_t timeout = 0;
120 
121 	/* retrieve network credentials without BSSID */
122 	err = wifi_credentials_get_by_ssid_personal(
123 		SSID1, sizeof(SSID1), &security, bssid_buf, ARRAY_SIZE(bssid_buf), psk_buf,
124 		ARRAY_SIZE(psk_buf), &psk_len, &flags, &channel, &timeout);
125 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
126 	zassert_equal(strncmp(PSK1, psk_buf, ARRAY_SIZE(psk_buf)), 0, "PSK mismatch");
127 	zassert_equal(flags, 0, "Flags mismatch");
128 	zassert_equal(channel, 0, "Channel mismatch");
129 	zassert_equal(security, SECURITY1, "Security type mismatch");
130 
131 	err = wifi_credentials_delete_by_ssid(SSID1, sizeof(SSID1));
132 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
133 }
134 
135 /* Verify that we can successfully set/get a network with a fixed BSSID. */
ZTEST(wifi_credentials,test_single_with_bssid)136 ZTEST(wifi_credentials, test_single_with_bssid)
137 {
138 	int err;
139 
140 	/* set network credentials with BSSID */
141 	err = wifi_credentials_set_personal(SSID1, sizeof(SSID1), SECURITY1, BSSID1, 6, PSK1,
142 					    sizeof(PSK1), FLAGS1, CHANNEL1, 0);
143 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
144 
145 	enum wifi_security_type security = -1;
146 	uint8_t bssid_buf[WIFI_MAC_ADDR_LEN] = "";
147 	char psk_buf[WIFI_CREDENTIALS_MAX_PASSWORD_LEN] = "";
148 	size_t psk_len = 0;
149 	uint32_t flags = 0;
150 	uint8_t channel = 0;
151 	uint32_t timeout = 0;
152 
153 	/* retrieve network credentials with BSSID */
154 	err = wifi_credentials_get_by_ssid_personal(
155 		SSID1, sizeof(SSID1), &security, bssid_buf, ARRAY_SIZE(bssid_buf), psk_buf,
156 		ARRAY_SIZE(psk_buf), &psk_len, &flags, &channel, &timeout);
157 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
158 	zassert_equal(strncmp(PSK1, psk_buf, ARRAY_SIZE(psk_buf)), 0, "PSK mismatch");
159 	zassert_equal(psk_len, sizeof(PSK1), "PSK length mismatch");
160 	zassert_equal(strncmp(BSSID1, bssid_buf, ARRAY_SIZE(bssid_buf)), 0, "BSSID mismatch");
161 	zassert_equal(flags, WIFI_CREDENTIALS_FLAG_BSSID, "Flags mismatch");
162 	zassert_equal(channel, CHANNEL1, "Channel mismatch");
163 	zassert_equal(security, SECURITY1, "Security type mismatch");
164 
165 	err = wifi_credentials_delete_by_ssid(SSID1, sizeof(SSID1));
166 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
167 }
168 
169 /* Verify that we can successfully set/get an open network. */
ZTEST(wifi_credentials,test_single_without_psk)170 ZTEST(wifi_credentials, test_single_without_psk)
171 {
172 	int err;
173 
174 	/* set network credentials without PSK/BSSID */
175 	err = wifi_credentials_set_personal(SSID2, sizeof(SSID2), SECURITY2, NULL, 6, NULL, 0,
176 					    FLAGS2, CHANNEL2, 0);
177 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
178 
179 	enum wifi_security_type security = -1;
180 	uint8_t bssid_buf[WIFI_MAC_ADDR_LEN] = "";
181 	char psk_buf[WIFI_CREDENTIALS_MAX_PASSWORD_LEN] = "";
182 	size_t psk_len = 0;
183 	uint32_t flags = 0;
184 	uint8_t channel = 0;
185 	uint32_t timeout = 0;
186 
187 	/* retrieve network credentials without PSK/BSSID */
188 	err = wifi_credentials_get_by_ssid_personal(
189 		SSID2, sizeof(SSID2), &security, bssid_buf, ARRAY_SIZE(bssid_buf), psk_buf,
190 		ARRAY_SIZE(psk_buf), &psk_len, &flags, &channel, &timeout);
191 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
192 	zassert_equal(psk_len, 0, "PSK length mismatch");
193 	zassert_equal(flags, 0, "Flags mismatch");
194 	zassert_equal(channel, CHANNEL2, "Channel mismatch");
195 
196 	err = wifi_credentials_delete_by_ssid(SSID2, sizeof(SSID2));
197 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
198 }
199 
200 /* Verify that we can set/get a network that is only identified by a BSSID. */
ZTEST(wifi_credentials,test_single_without_ssid)201 ZTEST(wifi_credentials, test_single_without_ssid)
202 {
203 	int err;
204 
205 	err = wifi_credentials_set_personal("", 0, SECURITY1, BSSID1, 6, PSK1, sizeof(PSK1), FLAGS1,
206 					    CHANNEL1, 0);
207 	zassert_equal(err, -EINVAL, "Expected -EINVAL, got %d", err);
208 
209 	enum wifi_security_type security = -1;
210 	uint8_t bssid_buf[WIFI_MAC_ADDR_LEN] = "";
211 	char psk_buf[WIFI_CREDENTIALS_MAX_PASSWORD_LEN] = "";
212 	size_t psk_len = 0;
213 	uint32_t flags = 0;
214 	uint8_t channel = 0;
215 	uint32_t timeout = 0;
216 
217 	err = wifi_credentials_get_by_ssid_personal(
218 		"", 0, &security, bssid_buf, ARRAY_SIZE(bssid_buf), psk_buf, ARRAY_SIZE(psk_buf),
219 		&psk_len, &flags, &channel, &timeout);
220 	zassert_equal(err, -EINVAL, "Expected -EINVAL, got %d", err);
221 
222 	err = wifi_credentials_delete_by_ssid("", 0);
223 	zassert_equal(err, -EINVAL, "Expected -EINVAL, got %d", err);
224 }
225 
226 /* Verify that we can handle SSIDs that contain NULL characters. */
ZTEST(wifi_credentials,test_single_garbled_ssid)227 ZTEST(wifi_credentials, test_single_garbled_ssid)
228 {
229 	int err;
230 
231 	err = wifi_credentials_set_personal(SSID4, sizeof(SSID4), SECURITY4, BSSID4, 6, PSK4,
232 					    sizeof(PSK4), FLAGS4, CHANNEL4, 0);
233 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
234 
235 	enum wifi_security_type security = -1;
236 	uint8_t bssid_buf[WIFI_MAC_ADDR_LEN] = "";
237 	char psk_buf[WIFI_CREDENTIALS_MAX_PASSWORD_LEN] = "";
238 	size_t psk_len = 0;
239 	uint32_t flags = 0;
240 	uint8_t channel = 0;
241 	uint32_t timeout = 0;
242 
243 	err = wifi_credentials_get_by_ssid_personal(
244 		SSID4, sizeof(SSID4), &security, bssid_buf, ARRAY_SIZE(bssid_buf), psk_buf,
245 		ARRAY_SIZE(psk_buf), &psk_len, &flags, &channel, &timeout);
246 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
247 	zassert_equal(strncmp(PSK4, psk_buf, ARRAY_SIZE(psk_buf)), 0, "PSK mismatch");
248 	zassert_equal(psk_len, sizeof(PSK4), "PSK length mismatch");
249 	zassert_equal(strncmp(BSSID4, bssid_buf, ARRAY_SIZE(bssid_buf)), 0, "BSSID mismatch");
250 	zassert_equal(security, SECURITY4, "Security type mismatch");
251 	zassert_equal(flags, FLAGS4, "Flags mismatch");
252 	zassert_equal(channel, CHANNEL4, "Channel mismatch");
253 
254 	err = wifi_credentials_delete_by_ssid(SSID4, sizeof(SSID4));
255 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
256 }
257 
258 /* Helper function for test_set_storage_limit, making sure that the SSID cache is correct. */
verify_ssid_cache_cb(void * cb_arg,const char * ssid,size_t ssid_len)259 void verify_ssid_cache_cb(void *cb_arg, const char *ssid, size_t ssid_len)
260 {
261 	static int call_count;
262 	static const char *const ssids[] = {SSID3, SSID2};
263 
264 	zassert_equal(strncmp(ssids[call_count++], ssid, ssid_len), 0, "SSID cache mismatch");
265 	zassert_is_null(cb_arg, "Callback argument is not NULL");
266 }
267 
268 /* Verify that wifi_credentials behaves correctly when the storage limit is reached. */
ZTEST(wifi_credentials,test_storage_limit)269 ZTEST(wifi_credentials, test_storage_limit)
270 {
271 	int err;
272 
273 	/* Set two networks */
274 	err = wifi_credentials_set_personal(SSID1, sizeof(SSID1), SECURITY1, BSSID1, 6, PSK1,
275 					    sizeof(PSK1), FLAGS1, CHANNEL1, 0);
276 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
277 
278 	err = wifi_credentials_set_personal(SSID2, sizeof(SSID2), SECURITY2, NULL, 6, NULL, 0,
279 					    FLAGS2, CHANNEL2, 0);
280 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
281 
282 	enum wifi_security_type security = -1;
283 	uint8_t bssid_buf[WIFI_MAC_ADDR_LEN] = "";
284 	char psk_buf[WIFI_CREDENTIALS_MAX_PASSWORD_LEN] = "";
285 	size_t psk_len = 0;
286 	uint32_t flags = 0;
287 	uint8_t channel = 0;
288 	uint32_t timeout = 0;
289 
290 	/* Get two networks */
291 	err = wifi_credentials_get_by_ssid_personal(
292 		SSID1, sizeof(SSID1), &security, bssid_buf, ARRAY_SIZE(bssid_buf), psk_buf,
293 		ARRAY_SIZE(psk_buf), &psk_len, &flags, &channel, &timeout);
294 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
295 	zassert_equal(strncmp(PSK1, psk_buf, ARRAY_SIZE(psk_buf)), 0, "PSK mismatch");
296 	zassert_equal(psk_len, sizeof(PSK1), "PSK length mismatch");
297 	zassert_equal(strncmp(BSSID1, bssid_buf, ARRAY_SIZE(bssid_buf)), 0, "BSSID mismatch");
298 	zassert_equal(security, SECURITY1, "Security type mismatch");
299 	zassert_equal(flags, FLAGS1, "Flags mismatch");
300 	zassert_equal(channel, CHANNEL1, "Channel mismatch");
301 
302 	err = wifi_credentials_get_by_ssid_personal(
303 		SSID2, sizeof(SSID2), &security, bssid_buf, ARRAY_SIZE(bssid_buf), psk_buf,
304 		ARRAY_SIZE(psk_buf), &psk_len, &flags, &channel, &timeout);
305 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
306 	zassert_equal(security, SECURITY2, "Security type mismatch");
307 	zassert_equal(flags, FLAGS2, "Flags mismatch");
308 	zassert_equal(channel, CHANNEL2, "Channel mismatch");
309 
310 	/* Set third network */
311 	err = wifi_credentials_set_personal(SSID3, sizeof(SSID3), SECURITY3, NULL, 6, PSK3,
312 					    sizeof(PSK3), FLAGS3, CHANNEL3, 0);
313 	zassert_equal(err, -ENOBUFS, "Expected -ENOBUFS, got %d", err);
314 
315 	/* Not enough space? Delete the first one. */
316 	wifi_credentials_delete_by_ssid(SSID1, ARRAY_SIZE(SSID1));
317 	err = wifi_credentials_set_personal(SSID3, sizeof(SSID3), SECURITY3, NULL, 6, PSK3,
318 					    sizeof(PSK3), FLAGS3, CHANNEL3, 0);
319 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
320 
321 	err = wifi_credentials_get_by_ssid_personal(
322 		SSID3, sizeof(SSID3), &security, bssid_buf, ARRAY_SIZE(bssid_buf), psk_buf,
323 		ARRAY_SIZE(psk_buf), &psk_len, &flags, &channel, &timeout);
324 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
325 	zassert_equal(security, SECURITY3, "Security type mismatch");
326 	zassert_equal(psk_len, sizeof(PSK3), "PSK length mismatch");
327 	zassert_equal(strncmp(PSK3, psk_buf, ARRAY_SIZE(psk_buf)), 0, "PSK mismatch");
328 	zassert_equal(flags, FLAGS3, "Flags mismatch");
329 	zassert_equal(channel, CHANNEL3, "Channel mismatch");
330 
331 	wifi_credentials_for_each_ssid(verify_ssid_cache_cb, NULL);
332 }
333 
334 /* Verify that all entries are deleted. */
ZTEST(wifi_credentials,test_delete_all_entries)335 ZTEST(wifi_credentials, test_delete_all_entries)
336 {
337 	/* Set two networks */
338 	int err = wifi_credentials_set_personal(SSID1, sizeof(SSID1), SECURITY1, BSSID1, 6, PSK1,
339 						sizeof(PSK1), FLAGS1, CHANNEL1, 0);
340 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
341 
342 	err = wifi_credentials_set_personal(SSID2, sizeof(SSID2), SECURITY2, NULL, 6, NULL, 0,
343 					    FLAGS2, CHANNEL2, 0);
344 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
345 
346 	/* Delete all networks */
347 	err = wifi_credentials_delete_all();
348 	zassert_equal(err, EXIT_SUCCESS, "Expected EXIT_SUCCESS, got %d", err);
349 
350 	/* Verify that the storage is empty */
351 	zassert_true(wifi_credentials_is_empty(), "Storage is not empty");
352 }
353 
354 ZTEST_SUITE(wifi_credentials, NULL, NULL, wifi_credentials_setup, wifi_credentials_teardown, NULL);
355