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