1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/sys/byteorder.h>
9 #include <zephyr/net_buf.h>
10 #include <zephyr/net/net_ip.h>
11 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
12 #include <zephyr/mgmt/mcumgr/transport/smp_dummy.h>
13 #include <zephyr/mgmt/mcumgr/grp/fs_mgmt/fs_mgmt.h>
14 #include <zcbor_common.h>
15 #include <zcbor_decode.h>
16 #include <mgmt/mcumgr/transport/smp_internal.h>
17 
18 #define SMP_RESPONSE_WAIT_TIME 3
19 
20 /* Test fs_mgmt supported hash/checksum query command */
21 static const uint8_t command[] = {
22 	0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x01, 0x03,
23 	0xbf, 0xff,
24 };
25 
26 struct hash_checksum_type {
27 	uint8_t name[32];
28 	uint32_t format;
29 	uint32_t size;
30 	bool found;
31 	bool entries_matched;
32 };
33 
ZTEST(fs_mgmt_hash_supported,test_supported)34 ZTEST(fs_mgmt_hash_supported, test_supported)
35 {
36 	struct net_buf *nb;
37 	struct hash_checksum_type expected_types[] = {
38 #ifdef CONFIG_MCUMGR_GRP_FS_HASH_SHA256
39 
40 		{
41 			.name = "sha256",
42 			.format = 1,
43 			.size = 32,
44 			.found = false,
45 			.entries_matched = false,
46 		},
47 #endif
48 #ifdef CONFIG_MCUMGR_GRP_FS_CHECKSUM_IEEE_CRC32
49 		{
50 			.name = "crc32",
51 			.format = 0,
52 			.size = 4,
53 			.found = false,
54 			.entries_matched = false,
55 		},
56 #endif
57 	};
58 
59 	/* Enable dummy SMP backend and ready for usage */
60 	smp_dummy_enable();
61 	smp_dummy_clear_state();
62 
63 	/* Send test echo command to dummy SMP backend */
64 	(void)smp_dummy_tx_pkt(command, sizeof(command));
65 	smp_dummy_add_data();
66 
67 	/* For a short duration to see if response has been received */
68 	bool received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME);
69 
70 	zassert_true(received, "Expected to receive data but timed out\n");
71 
72 	/* Retrieve response buffer and ensure validity */
73 	nb = smp_dummy_get_outgoing();
74 	smp_dummy_disable();
75 
76 	/* Check that the headers seem valid */
77 	struct smp_hdr *response_hdr = (struct smp_hdr *)nb->data;
78 	uint16_t len = ntohs(response_hdr->nh_len);
79 	uint16_t group = ntohs(response_hdr->nh_group);
80 
81 	zassert_equal(response_hdr->nh_op, MGMT_OP_READ_RSP,
82 		      "Expected response to have rease response type");
83 	zassert_true((len > 20), "Expected response to be at least 20 bytes in length");
84 	zassert_equal(group, MGMT_GROUP_ID_FS, "Expected response to be FS group");
85 	zassert_equal(response_hdr->nh_id, FS_MGMT_ID_SUPPORTED_HASH_CHECKSUM,
86 		      "Expected response to be supported hash/checksum ID");
87 
88 	/* Process the payload with zcbor and check expected types are present */
89 	zcbor_state_t state[10];
90 	struct zcbor_string key;
91 	uint32_t format_value;
92 	bool format_found;
93 	uint32_t size_value;
94 	bool size_found;
95 	int8_t entry = 0;
96 	bool ok = true;
97 
98 	/* Search expected type array for this type and update details */
99 	zcbor_new_decode_state(state, 10, &nb->data[sizeof(struct smp_hdr)],
100 			       (nb->len - sizeof(struct smp_hdr)), 1, NULL, 0);
101 
102 	ok = zcbor_map_start_decode(state);
103 
104 	ok = zcbor_tstr_decode(state, &key);
105 
106 	zassert_equal(key.len, strlen("types"),
107 		      "Expected CBOR response 'types' value length to match");
108 	zassert_mem_equal(key.value, "types", strlen("types"),
109 			  "Expected CBOR response 'types' value to match");
110 
111 	ok = zcbor_map_start_decode(state);
112 
113 	while (ok == true) {
114 		ok = zcbor_tstr_decode(state, &key);
115 		if (!ok) {
116 			break;
117 		}
118 
119 		entry = 0;
120 		while (entry < ARRAY_SIZE(expected_types)) {
121 			if (memcmp(key.value, expected_types[entry].name, MIN(key.len,
122 					strlen(expected_types[entry].name))) == 0) {
123 				zassert_equal(expected_types[entry].found, false,
124 					      "Found entry multiple times");
125 				expected_types[entry].found = true;
126 				break;
127 			}
128 
129 			++entry;
130 		}
131 
132 		zassert_equal(entry < ARRAY_SIZE(expected_types), true,
133 			      "Did not find entry for type");
134 
135 		ok = zcbor_map_start_decode(state);
136 		format_found = false;
137 		size_found = false;
138 
139 		while (ok == true) {
140 			ok = zcbor_tstr_decode(state, &key);
141 
142 			if (!ok) {
143 				break;
144 			}
145 
146 			if (memcmp(key.value, "format", strlen("format")) == 0) {
147 				zassert_false(format_found,
148 					      "Expected format to only be found once");
149 				ok &= zcbor_uint32_decode(state, &format_value);
150 				format_found = true;
151 			} else if (memcmp(key.value, "size", strlen("size")) == 0) {
152 				zassert_false(size_found, "Expected size to be only found once");
153 				ok &= zcbor_uint32_decode(state, &size_value);
154 				size_found = true;
155 			} else {
156 				zassert_true(false, "Unexpected field in CBOR response");
157 			}
158 
159 			if (format_found == true && size_found == true) {
160 				zassert_equal(expected_types[entry].format, format_value,
161 					      "Format value mismatch with expected value");
162 				zassert_equal(expected_types[entry].size, size_value,
163 					      "Size value mismatch with expected value");
164 				expected_types[entry].entries_matched = true;
165 			}
166 		}
167 
168 		ok = zcbor_map_end_decode(state);
169 	}
170 
171 	ok = zcbor_map_end_decode(state);
172 }
173 
174 ZTEST_SUITE(fs_mgmt_hash_supported, NULL, NULL, NULL, NULL, NULL);
175