/src/wireshark/epan/dissectors/packet-rf4ce-secur.c
Line | Count | Source |
1 | | /* packet-rf4ce-secur.c |
2 | | * Security related functions and objects for RF4CE dissector |
3 | | * Copyright (C) Atmosic 2023 |
4 | | * |
5 | | * Wireshark - Network traffic analyzer |
6 | | * By Gerald Combs <gerald@wireshark.org> |
7 | | * Copyright 1998 Gerald Combs |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
12 | | #include "packet-rf4ce-secur.h" |
13 | | #include "packet-zbee-security.h" |
14 | | #include "packet-ieee802154.h" |
15 | | #include <wsutil/wsgcrypt.h> |
16 | | #include <epan/proto_data.h> |
17 | | |
18 | | #ifdef RF4CE_DEBUG_EN |
19 | | void rf4ce_print_arr(const char *str, uint8_t *ptr, uint16_t len); |
20 | | #define RF4CE_PRINT_ARR(s, p, l) rf4ce_print_arr(s, p, l) |
21 | | #else |
22 | | #define RF4CE_PRINT_ARR(s, p, l) |
23 | | #endif /* RF4CE_DEBUG_EN */ |
24 | | |
25 | | static keypair_context_t keypair_context; |
26 | | static key_exchange_context_t key_exchange_context; |
27 | | static addr_entry_t addr_table[RF4CE_ADDR_TABLE_SIZE]; |
28 | | static nwk_key_entry_t nwk_key_storage[RF4CE_NWK_KEY_STORAGE_SIZE]; |
29 | | static vendor_secret_entry_t vendor_secret_storage[RF4CE_VENDOR_SECRET_STORAGE_SIZE]; |
30 | | |
31 | | static void keypair_context_calc_key(uint8_t *nwk_key); |
32 | | static nwk_key_entry_t *nwk_key_storage_get_entry_by_key(uint8_t *nwk_key, bool key_from_gui); |
33 | | |
34 | | static void reverse(uint8_t *dest, uint8_t *src, uint16_t size); |
35 | | |
36 | | /* RF4CE GDP 2.0 spec, part 7.4.1 Key Exchange negotiation |
37 | | * Default secret: This is a 128bit "secret" that is known to all devices that are certified to |
38 | | * conform to this specification. The value shall be set to the following octet string (lowest order |
39 | | * octet first) |
40 | | * Note that this value should be expected to be widely known and the overall link security |
41 | | * should not depend on this value remaining a secret. |
42 | | */ |
43 | | uint8_t DEFAULT_SECRET[SEC_STR_LEN] = |
44 | | {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, |
45 | | 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; |
46 | | |
47 | | void keypair_context_init(const uint8_t *controller_ieee, const uint8_t *target_ieee, uint8_t expected_transfer_count) |
48 | 4 | { |
49 | 4 | if ((controller_ieee == NULL) || (target_ieee == NULL)) |
50 | 3 | { |
51 | 3 | return; |
52 | 3 | } |
53 | 1 | memset(&keypair_context, 0, sizeof(keypair_context_t)); |
54 | | |
55 | 1 | memcpy(keypair_context.controller_addr, controller_ieee, RF4CE_IEEE_ADDR_LEN); |
56 | 1 | memcpy(keypair_context.target_addr, target_ieee, RF4CE_IEEE_ADDR_LEN); |
57 | | |
58 | 1 | keypair_context.nwk_key_exchange_transfer_expected = expected_transfer_count; |
59 | 1 | } |
60 | | |
61 | | static void keypair_context_calc_key(uint8_t *nwk_key) |
62 | 0 | { |
63 | 0 | for (int i = 0; i < keypair_context.nwk_key_exchange_transfer_received; i++) |
64 | 0 | { |
65 | 0 | for (int j = 0; j < KEY_LEN; j++) |
66 | 0 | { |
67 | 0 | keypair_context.nwk_key_seed[(i + 1) * KEY_LEN + j] ^= keypair_context.nwk_key_seed[i * KEY_LEN + j]; |
68 | 0 | } |
69 | 0 | } |
70 | |
|
71 | 0 | memcpy(nwk_key, &keypair_context.nwk_key_seed[RF4CE_NWK_KEY_SEED_DATA_LENGTH - KEY_LEN], KEY_LEN); |
72 | 0 | } |
73 | | |
74 | | void keypair_context_update_seed(uint8_t *seed, uint8_t seed_seqn) |
75 | 0 | { |
76 | 0 | bool is_retransmit = (seed_seqn == keypair_context.nwk_key_exchange_transfer_received - 1); |
77 | 0 | bool is_latest_seed = (seed_seqn + 1 == keypair_context.nwk_key_exchange_transfer_expected); |
78 | | |
79 | | /* retransmitt of the latest key seed - we must to re-calculate a NWK key */ |
80 | 0 | if (is_retransmit && is_latest_seed) |
81 | 0 | { |
82 | 0 | memcpy(keypair_context.nwk_key_seed, keypair_context.nwk_key_seed_prev, RF4CE_NWK_KEY_SEED_DATA_LENGTH); |
83 | 0 | } |
84 | |
|
85 | 0 | if (seed_seqn == 0) |
86 | 0 | { |
87 | 0 | memcpy(keypair_context.nwk_key_seed_latest, seed, RF4CE_NWK_KEY_SEED_DATA_LENGTH); |
88 | 0 | keypair_context.nwk_key_exchange_transfer_received = 1; |
89 | 0 | return; |
90 | 0 | } |
91 | | |
92 | | /* Retransmit of the previous key seed. Should take this one */ |
93 | 0 | if (is_retransmit) |
94 | 0 | { |
95 | | /* save this one as a candidate */ |
96 | 0 | memcpy(keypair_context.nwk_key_seed_latest, seed, RF4CE_NWK_KEY_SEED_DATA_LENGTH); |
97 | | |
98 | | /* move on if it's the latest seed to re-calculate a NWK key */ |
99 | 0 | if (!is_latest_seed) |
100 | 0 | { |
101 | 0 | return; |
102 | 0 | } |
103 | 0 | } |
104 | | |
105 | 0 | if (seed_seqn == keypair_context.nwk_key_exchange_transfer_received) |
106 | 0 | { |
107 | | /* Apply previous key seed, it has been accepted since we received the next one */ |
108 | 0 | for (int i = 0; i < RF4CE_NWK_KEY_SEED_DATA_LENGTH; i++) |
109 | 0 | { |
110 | 0 | keypair_context.nwk_key_seed[i] ^= keypair_context.nwk_key_seed_latest[i]; |
111 | 0 | } |
112 | | |
113 | | /* save this one as a candidate */ |
114 | 0 | memcpy(keypair_context.nwk_key_seed_latest, seed, RF4CE_NWK_KEY_SEED_DATA_LENGTH); |
115 | 0 | keypair_context.nwk_key_exchange_transfer_received += 1; |
116 | 0 | } |
117 | |
|
118 | 0 | if (is_latest_seed) |
119 | 0 | { |
120 | 0 | uint8_t nwk_key[KEY_LEN] = {0}; |
121 | 0 | addr_entry_t *controller_addr_ent = rf4ce_addr_table_get_addr_entry_by_ieee(keypair_context.controller_addr); |
122 | 0 | addr_entry_t *target_addr_ent = rf4ce_addr_table_get_addr_entry_by_ieee(keypair_context.target_addr); |
123 | | |
124 | | /* save the current key seed to avoid retransmitts of the latest one in future */ |
125 | 0 | memcpy(keypair_context.nwk_key_seed_prev, keypair_context.nwk_key_seed, RF4CE_NWK_KEY_SEED_DATA_LENGTH); |
126 | |
|
127 | 0 | for (int i = 0; i < RF4CE_NWK_KEY_SEED_DATA_LENGTH; i++) |
128 | 0 | { |
129 | 0 | keypair_context.nwk_key_seed[i] ^= keypair_context.nwk_key_seed_latest[i]; |
130 | 0 | } |
131 | |
|
132 | 0 | keypair_context_calc_key(nwk_key); |
133 | |
|
134 | 0 | nwk_key_storage_add_entry( |
135 | 0 | nwk_key, |
136 | 0 | controller_addr_ent, |
137 | 0 | target_addr_ent, |
138 | 0 | false, /* key from commissioning session */ |
139 | 0 | true); /* is_pairing_key */ |
140 | 0 | } |
141 | 0 | } |
142 | | |
143 | | static nwk_key_entry_t *nwk_key_storage_get_entry_by_key(uint8_t *nwk_key, bool key_from_gui) |
144 | 0 | { |
145 | 0 | nwk_key_entry_t *entry = NULL; |
146 | 0 | int idx = 0; |
147 | |
|
148 | 0 | while (idx < RF4CE_NWK_KEY_STORAGE_SIZE) |
149 | 0 | { |
150 | 0 | if (nwk_key_storage[idx].is_used && (nwk_key_storage[idx].key_from_gui == key_from_gui) && (memcmp(nwk_key_storage[idx].nwk_key, nwk_key, KEY_LEN) == 0)) |
151 | 0 | { |
152 | 0 | entry = nwk_key_storage + idx; |
153 | 0 | break; |
154 | 0 | } |
155 | | |
156 | 0 | idx++; |
157 | 0 | } |
158 | |
|
159 | 0 | return entry; |
160 | 0 | } |
161 | | |
162 | | void nwk_key_storage_add_entry(uint8_t *nwk_key, addr_entry_t *controller_addr_ent, addr_entry_t *target_addr_ent, bool key_from_gui, bool is_pairing_key) |
163 | 0 | { |
164 | | /* find an existing entry so as not to add duplicates */ |
165 | 0 | nwk_key_entry_t *nwk_key_entry = nwk_key_storage_get_entry_by_key(nwk_key, key_from_gui); |
166 | |
|
167 | 0 | if (nwk_key_entry == NULL) |
168 | 0 | { |
169 | 0 | int idx = 0; |
170 | |
|
171 | 0 | while (idx < RF4CE_NWK_KEY_STORAGE_SIZE) |
172 | 0 | { |
173 | 0 | if (!nwk_key_storage[idx].is_used) |
174 | 0 | { |
175 | 0 | memcpy(nwk_key_storage[idx].nwk_key, nwk_key, KEY_LEN); |
176 | 0 | nwk_key_storage[idx].controller_addr_ent = controller_addr_ent; |
177 | 0 | nwk_key_storage[idx].target_addr_ent = target_addr_ent; |
178 | 0 | nwk_key_storage[idx].key_from_gui = key_from_gui; |
179 | 0 | nwk_key_storage[idx].is_used = true; |
180 | 0 | nwk_key_storage[idx].is_pairing_key = is_pairing_key; |
181 | 0 | break; |
182 | 0 | } |
183 | | |
184 | 0 | idx++; |
185 | 0 | } |
186 | 0 | } |
187 | 0 | } |
188 | | |
189 | | void nwk_key_storage_release_entry(uint8_t *nwk_key, bool key_from_gui) |
190 | 0 | { |
191 | 0 | nwk_key_entry_t *nwk_key_entry = nwk_key_storage_get_entry_by_key(nwk_key, key_from_gui); |
192 | |
|
193 | 0 | if (nwk_key_entry != NULL) |
194 | 0 | { |
195 | 0 | nwk_key_entry->is_used = false; |
196 | 0 | } |
197 | 0 | } |
198 | | |
199 | | void rf4ce_addr_table_add_addrs(const void *ieee_addr, uint16_t short_addr) |
200 | 16 | { |
201 | 16 | unsigned idx = 0; |
202 | | |
203 | 16 | if (ieee_addr == NULL) |
204 | 2 | { |
205 | 2 | return; |
206 | 2 | } |
207 | | |
208 | | /* search for addresses so as not to add duplicates */ |
209 | 1.44k | while (idx < RF4CE_ADDR_TABLE_SIZE) |
210 | 1.43k | { |
211 | 1.43k | if (addr_table[idx].is_used && (memcmp(addr_table[idx].ieee_addr, ieee_addr, RF4CE_IEEE_ADDR_LEN) == 0) && addr_table[idx].short_addr == short_addr) |
212 | 3 | { |
213 | 3 | return; |
214 | 3 | } |
215 | | |
216 | 1.42k | idx++; |
217 | 1.42k | } |
218 | | |
219 | | /* no duplicates found, search for a free slot */ |
220 | 11 | idx = 0; |
221 | 66 | while (idx < RF4CE_ADDR_TABLE_SIZE && addr_table[idx].is_used) |
222 | 55 | { |
223 | 55 | idx++; |
224 | 55 | } |
225 | | |
226 | 11 | if (idx < RF4CE_ADDR_TABLE_SIZE) |
227 | 11 | { |
228 | 11 | memcpy(addr_table[idx].ieee_addr, ieee_addr, RF4CE_IEEE_ADDR_LEN); |
229 | 11 | addr_table[idx].short_addr = short_addr; |
230 | 11 | addr_table[idx].is_used = true; |
231 | 11 | } |
232 | 11 | } |
233 | | |
234 | | bool rf4ce_addr_table_get_ieee_addr(uint8_t *ieee_addr, packet_info *pinfo, bool is_src) |
235 | 152 | { |
236 | 152 | bool addr_found = false; |
237 | 152 | address_type addr_type; |
238 | 152 | ieee802154_hints_t *hints; |
239 | 152 | const void *p_addr = NULL; |
240 | 152 | uint16_t short_addr = 0xffff; |
241 | | |
242 | | /* Check inputs */ |
243 | 152 | if ((ieee_addr == NULL) || (pinfo == NULL)) |
244 | 0 | { |
245 | 0 | return false; |
246 | 0 | } |
247 | 152 | if (is_src) |
248 | 76 | { |
249 | 76 | addr_type = pinfo->dl_src.type; |
250 | 76 | p_addr = pinfo->dl_src.data; |
251 | 76 | } |
252 | 76 | else |
253 | 76 | { |
254 | 76 | addr_type = pinfo->dl_dst.type; |
255 | 76 | p_addr = pinfo->dl_dst.data; |
256 | 76 | } |
257 | 152 | if (addr_type == AT_EUI64) |
258 | 29 | { |
259 | 29 | if (p_addr == NULL) |
260 | 0 | { |
261 | 0 | return false; |
262 | 0 | } |
263 | 29 | } |
264 | 123 | else |
265 | 123 | { |
266 | | /* Get addresses */ |
267 | 123 | hints = (ieee802154_hints_t *)p_get_proto_data(wmem_file_scope(), |
268 | 123 | pinfo, |
269 | 123 | proto_get_id_by_filter_name(IEEE802154_PROTOABBREV_WPAN), |
270 | 123 | 0 |
271 | 123 | ); |
272 | 123 | if (hints == NULL) |
273 | 0 | { |
274 | 0 | return false; |
275 | 0 | } |
276 | 123 | short_addr = (is_src) ? hints->src16 : hints->dst16; |
277 | 123 | } |
278 | | /* Search address in address table */ |
279 | 19.4k | for (unsigned idx = 0; idx < RF4CE_ADDR_TABLE_SIZE; idx++) |
280 | 19.3k | { |
281 | 19.3k | if (addr_table[idx].is_used) |
282 | 572 | { |
283 | 572 | if (addr_type == AT_EUI64) |
284 | 81 | { |
285 | 81 | if (memcmp(addr_table[idx].ieee_addr, p_addr, RF4CE_IEEE_ADDR_LEN) == 0) { |
286 | 1 | addr_found = true; |
287 | 1 | } |
288 | 81 | } |
289 | 491 | else |
290 | 491 | { |
291 | 491 | if (addr_table[idx].short_addr == short_addr) { |
292 | 0 | addr_found = true; |
293 | 0 | } |
294 | 491 | } |
295 | 572 | if (addr_found) |
296 | 1 | { |
297 | 1 | memcpy(ieee_addr, addr_table[idx].ieee_addr, RF4CE_IEEE_ADDR_LEN); |
298 | 1 | break; |
299 | 1 | } |
300 | 572 | } |
301 | 19.3k | } |
302 | 152 | return addr_found; |
303 | 152 | } |
304 | | |
305 | | addr_entry_t *rf4ce_addr_table_get_addr_entry_by_ieee(uint8_t *ieee_addr) |
306 | 0 | { |
307 | 0 | addr_entry_t *entry = NULL; |
308 | 0 | unsigned idx = 0; |
309 | |
|
310 | 0 | while (ieee_addr != NULL && idx < RF4CE_ADDR_TABLE_SIZE) |
311 | 0 | { |
312 | 0 | if (addr_table[idx].is_used && memcmp(addr_table[idx].ieee_addr, ieee_addr, RF4CE_IEEE_ADDR_LEN) == 0) |
313 | 0 | { |
314 | 0 | entry = addr_table + idx; |
315 | 0 | break; |
316 | 0 | } |
317 | | |
318 | 0 | idx++; |
319 | 0 | } |
320 | |
|
321 | 0 | return entry; |
322 | 0 | } |
323 | | |
324 | | void key_exchange_context_init(void) |
325 | 0 | { |
326 | 0 | memset(&key_exchange_context.rand_a, 0, RF4CE_PROFILE_CMD_KEY_EXCHANGE_RAND_A_LENGTH); |
327 | 0 | memset(&key_exchange_context.rand_b, 0, RF4CE_PROFILE_CMD_KEY_EXCHANGE_RAND_B_LENGTH); |
328 | 0 | memset(&key_exchange_context.mac_a, 0, RF4CE_IEEE_ADDR_LEN); |
329 | 0 | memset(&key_exchange_context.mac_b, 0, RF4CE_IEEE_ADDR_LEN); |
330 | 0 | } |
331 | | |
332 | | void key_exchange_context_start_procedure(void) |
333 | 0 | { |
334 | 0 | if (!key_exchange_context.is_proc_started) |
335 | 0 | { |
336 | 0 | key_exchange_context.is_proc_started = true; |
337 | 0 | } |
338 | 0 | } |
339 | | |
340 | | void key_exchange_context_stop_procedure(void) |
341 | 0 | { |
342 | 0 | if (key_exchange_context.is_proc_started) |
343 | 0 | { |
344 | 0 | key_exchange_context.is_proc_started = false; |
345 | 0 | } |
346 | 0 | } |
347 | | |
348 | | bool key_exchange_context_is_procedure_started(void) |
349 | 0 | { |
350 | 0 | return key_exchange_context.is_proc_started; |
351 | 0 | } |
352 | | |
353 | | void key_exchange_context_set_rand_a(uint8_t *rand_a) |
354 | 0 | { |
355 | 0 | if (rand_a != NULL) |
356 | 0 | { |
357 | 0 | memcpy(key_exchange_context.rand_a, rand_a, RF4CE_PROFILE_CMD_KEY_EXCHANGE_RAND_A_LENGTH); |
358 | 0 | } |
359 | 0 | } |
360 | | |
361 | | void key_exchange_context_set_rand_b(uint8_t *rand_b) |
362 | 0 | { |
363 | 0 | if (rand_b != NULL) |
364 | 0 | { |
365 | 0 | memcpy(key_exchange_context.rand_b, rand_b, RF4CE_PROFILE_CMD_KEY_EXCHANGE_RAND_B_LENGTH); |
366 | 0 | } |
367 | 0 | } |
368 | | |
369 | | void key_exchange_context_set_mac_a(uint8_t *mac_a) |
370 | 0 | { |
371 | 0 | if (mac_a != NULL) |
372 | 0 | { |
373 | 0 | memcpy(key_exchange_context.mac_a, mac_a, RF4CE_IEEE_ADDR_LEN); |
374 | 0 | } |
375 | 0 | } |
376 | | |
377 | | void key_exchange_context_set_mac_b(uint8_t *mac_b) |
378 | 0 | { |
379 | 0 | if (mac_b != NULL) |
380 | 0 | { |
381 | 0 | memcpy(key_exchange_context.mac_b, mac_b, RF4CE_IEEE_ADDR_LEN); |
382 | 0 | } |
383 | 0 | } |
384 | | |
385 | | #ifdef RF4CE_DEBUG_EN |
386 | | void rf4ce_print_arr(const char *str, uint8_t *ptr, uint16_t len) |
387 | | { |
388 | | g_print("%s: ", str); |
389 | | for (uint16_t i = 0; i < len-1; i++) |
390 | | { |
391 | | g_print("%02x:", *(ptr+i)); |
392 | | } |
393 | | g_print("%02x\n", *(ptr+len-1)); |
394 | | } |
395 | | #endif /* RF4CE_DEBUG_EN */ |
396 | | |
397 | | static bool calc_key_cmac(uint8_t *secret, uint8_t *nwk_key, uint32_t tag_b_pack, uint8_t *key_out) |
398 | 0 | { |
399 | 0 | uint8_t mac_a[RF4CE_IEEE_ADDR_LEN]; |
400 | 0 | uint8_t mac_b[RF4CE_IEEE_ADDR_LEN]; |
401 | |
|
402 | 0 | uint8_t *rand_a = key_exchange_context.rand_a; |
403 | 0 | uint8_t *rand_b = key_exchange_context.rand_b; |
404 | |
|
405 | 0 | uint8_t k_dk_data[RF4CE_PROFILE_CMD_KEY_EXCHANGE_RAND_A_LENGTH + |
406 | 0 | RF4CE_PROFILE_CMD_KEY_EXCHANGE_RAND_B_LENGTH]; |
407 | 0 | uint8_t k_dk_data_reversed[RF4CE_PROFILE_CMD_KEY_EXCHANGE_RAND_B_LENGTH + |
408 | 0 | RF4CE_PROFILE_CMD_KEY_EXCHANGE_RAND_A_LENGTH]; |
409 | |
|
410 | 0 | uint8_t context_data[CONTEXT_STR_LEN + RF4CE_IEEE_ADDR_LEN + |
411 | 0 | RF4CE_IEEE_ADDR_LEN + KEY_LEN]; |
412 | |
|
413 | 0 | uint8_t k_dk_key[KEY_LEN]; |
414 | 0 | uint8_t new_key[KEY_LEN]; |
415 | |
|
416 | 0 | uint8_t dummy[KEY_LEN]; |
417 | 0 | uint32_t tag_b_calc; |
418 | |
|
419 | 0 | uint8_t *data_ptr; |
420 | |
|
421 | 0 | reverse(mac_a, key_exchange_context.mac_a, RF4CE_IEEE_ADDR_LEN); |
422 | 0 | reverse(mac_b, key_exchange_context.mac_b, RF4CE_IEEE_ADDR_LEN); |
423 | |
|
424 | 0 | data_ptr = k_dk_data; |
425 | 0 | memcpy(data_ptr, rand_a, RF4CE_PROFILE_CMD_KEY_EXCHANGE_RAND_A_LENGTH); |
426 | 0 | data_ptr += RF4CE_PROFILE_CMD_KEY_EXCHANGE_RAND_A_LENGTH; |
427 | 0 | memcpy(data_ptr, rand_b, RF4CE_PROFILE_CMD_KEY_EXCHANGE_RAND_B_LENGTH); |
428 | |
|
429 | 0 | data_ptr = k_dk_data_reversed; |
430 | 0 | memcpy(data_ptr, rand_b, RF4CE_PROFILE_CMD_KEY_EXCHANGE_RAND_B_LENGTH); |
431 | 0 | data_ptr += RF4CE_PROFILE_CMD_KEY_EXCHANGE_RAND_B_LENGTH; |
432 | 0 | memcpy(data_ptr, rand_a, RF4CE_PROFILE_CMD_KEY_EXCHANGE_RAND_A_LENGTH); |
433 | |
|
434 | 0 | data_ptr = context_data; |
435 | 0 | memcpy(data_ptr, CONTEXT_STR, CONTEXT_STR_LEN); |
436 | 0 | data_ptr += CONTEXT_STR_LEN; |
437 | 0 | memcpy(data_ptr, mac_a, RF4CE_IEEE_ADDR_LEN); |
438 | 0 | data_ptr += RF4CE_IEEE_ADDR_LEN; |
439 | 0 | memcpy(data_ptr, mac_b, RF4CE_IEEE_ADDR_LEN); |
440 | 0 | data_ptr += RF4CE_IEEE_ADDR_LEN; |
441 | 0 | memcpy(data_ptr, nwk_key, KEY_LEN); |
442 | | |
443 | | /* Generic Device Profile Version 2.0 |
444 | | * 7.4.2 Key generation |
445 | | * Calculate derivation key |
446 | | * K_dk = AES-128-CMAC (RAND-A || RAND-B, Shared secret) |
447 | | */ |
448 | 0 | rf4ce_aes_cmac(secret, SEC_STR_LEN, k_dk_data, k_dk_key); |
449 | | |
450 | | /* Calculate new link key |
451 | | * Link key = AES-128-CMAC (K_dk, context || label || pairing key) |
452 | | */ |
453 | 0 | rf4ce_aes_cmac(context_data, sizeof(context_data), k_dk_key, new_key); |
454 | | |
455 | | /* Calculate TAG-B value |
456 | | * TAG-B = AES-128-CMAC(link key, RAND-B || RAND-A) |
457 | | */ |
458 | 0 | rf4ce_aes_cmac(k_dk_data_reversed, sizeof(k_dk_data_reversed), new_key, dummy); |
459 | 0 | memcpy((uint8_t *)&tag_b_calc, dummy, RF4CE_PROFILE_CMD_KEY_EXCHANGE_TAG_A_LENGTH); |
460 | |
|
461 | 0 | RF4CE_PRINT_ARR("tag_b_calc", (uint8_t *)&tag_b_calc, 4); |
462 | 0 | RF4CE_PRINT_ARR(" new_key", new_key, 16); |
463 | |
|
464 | 0 | if (tag_b_pack == tag_b_calc) |
465 | 0 | { |
466 | 0 | memcpy(key_out, new_key, KEY_LEN); |
467 | 0 | return true; |
468 | 0 | } |
469 | | |
470 | 0 | return false; |
471 | 0 | } |
472 | | |
473 | | static bool key_exchange_calc_key_cont(uint8_t *secret, uint32_t tag_b_pack, bool try_pairing_key, uint8_t *new_key_out) |
474 | 0 | { |
475 | 0 | bool is_new_key_found = false; |
476 | |
|
477 | 0 | for (unsigned i = 0; i < RF4CE_NWK_KEY_STORAGE_SIZE; i++) |
478 | 0 | { |
479 | 0 | if (nwk_key_storage[i].is_used && ((try_pairing_key && nwk_key_storage[i].is_pairing_key) || (!try_pairing_key && nwk_key_storage[i].key_from_gui))) |
480 | 0 | { |
481 | 0 | is_new_key_found = calc_key_cmac(secret, nwk_key_storage[i].nwk_key, tag_b_pack, new_key_out); |
482 | |
|
483 | 0 | if (is_new_key_found) |
484 | 0 | { |
485 | 0 | break; |
486 | 0 | } |
487 | 0 | } |
488 | 0 | } |
489 | |
|
490 | 0 | return is_new_key_found; |
491 | 0 | } |
492 | | |
493 | | void key_exchange_calc_key(uint32_t tag_b_pack) |
494 | 0 | { |
495 | 0 | uint8_t *controller_addr = key_exchange_context.mac_a; |
496 | 0 | uint8_t *target_addr = key_exchange_context.mac_b; |
497 | |
|
498 | 0 | addr_entry_t *controller_addr_ent = rf4ce_addr_table_get_addr_entry_by_ieee(controller_addr); |
499 | 0 | addr_entry_t *target_addr_ent = rf4ce_addr_table_get_addr_entry_by_ieee(target_addr); |
500 | |
|
501 | 0 | uint8_t *secret; |
502 | |
|
503 | 0 | uint8_t new_key[KEY_LEN]; |
504 | 0 | bool is_new_key_found = false; |
505 | |
|
506 | 0 | for (unsigned i = 0; i < RF4CE_VENDOR_SECRET_STORAGE_SIZE; i++) |
507 | 0 | { |
508 | 0 | if (!vendor_secret_storage[i].is_used) |
509 | 0 | { |
510 | 0 | continue; |
511 | 0 | } |
512 | | |
513 | 0 | secret = vendor_secret_storage[i].secret; |
514 | | |
515 | | /* try all the pairing keys first */ |
516 | 0 | is_new_key_found = key_exchange_calc_key_cont(secret, tag_b_pack, true, new_key); |
517 | | |
518 | | /* try other keys */ |
519 | 0 | if (!is_new_key_found) |
520 | 0 | { |
521 | 0 | is_new_key_found = key_exchange_calc_key_cont(secret, tag_b_pack, false, new_key); |
522 | 0 | } |
523 | |
|
524 | 0 | if (is_new_key_found) |
525 | 0 | { |
526 | 0 | nwk_key_storage_add_entry( |
527 | 0 | new_key, |
528 | 0 | controller_addr_ent, |
529 | 0 | target_addr_ent, |
530 | 0 | false, /* key from the Key Exchange procedure */ |
531 | 0 | false); /* !is_pairing_key */ |
532 | |
|
533 | 0 | break; |
534 | 0 | } |
535 | 0 | } |
536 | 0 | } |
537 | | |
538 | | static vendor_secret_entry_t *vendor_secret_storage_get_entry(uint8_t *secret) |
539 | 15 | { |
540 | 15 | vendor_secret_entry_t *entry = NULL; |
541 | 15 | int idx = 0; |
542 | | |
543 | 975 | while (idx < RF4CE_VENDOR_SECRET_STORAGE_SIZE) |
544 | 960 | { |
545 | 960 | if (vendor_secret_storage[idx].is_used && (memcmp(vendor_secret_storage[idx].secret, secret, SEC_STR_LEN) == 0)) |
546 | 0 | { |
547 | 0 | entry = vendor_secret_storage + idx; |
548 | 0 | break; |
549 | 0 | } |
550 | | |
551 | 960 | idx++; |
552 | 960 | } |
553 | | |
554 | 15 | return entry; |
555 | 15 | } |
556 | | |
557 | | void vendor_secret_storage_add_entry(uint8_t *secret) |
558 | 15 | { |
559 | 15 | unsigned idx = 0; |
560 | 15 | vendor_secret_entry_t *entry = vendor_secret_storage_get_entry(secret); |
561 | | |
562 | 15 | if (entry != NULL) |
563 | 0 | { |
564 | 0 | return; |
565 | 0 | } |
566 | | |
567 | 15 | while (idx < RF4CE_VENDOR_SECRET_STORAGE_SIZE && vendor_secret_storage[idx].is_used) |
568 | 0 | { |
569 | 0 | idx++; |
570 | 0 | } |
571 | | |
572 | 15 | if (idx < RF4CE_VENDOR_SECRET_STORAGE_SIZE) |
573 | 15 | { |
574 | 15 | memcpy(vendor_secret_storage[idx].secret, secret, SEC_STR_LEN); |
575 | 15 | vendor_secret_storage[idx].is_used = true; |
576 | 15 | } |
577 | 15 | } |
578 | | |
579 | | void vendor_secret_storage_release_entry(uint8_t *secret) |
580 | 0 | { |
581 | 0 | vendor_secret_entry_t *entry = vendor_secret_storage_get_entry(secret); |
582 | |
|
583 | 0 | if (entry != NULL) |
584 | 0 | { |
585 | 0 | entry->is_used = false; |
586 | 0 | } |
587 | 0 | } |
588 | | |
589 | | void rf4ce_secur_cleanup(void) |
590 | 0 | { |
591 | 0 | int idx = 0; |
592 | |
|
593 | 0 | memset(&keypair_context, 0, sizeof(keypair_context)); |
594 | 0 | memset(addr_table, 0, sizeof(addr_table)); |
595 | |
|
596 | 0 | while (idx < RF4CE_NWK_KEY_STORAGE_SIZE) |
597 | 0 | { |
598 | 0 | if (nwk_key_storage[idx].is_used && !nwk_key_storage[idx].key_from_gui) |
599 | 0 | { |
600 | 0 | nwk_key_storage[idx].is_used = false; |
601 | 0 | } |
602 | |
|
603 | 0 | idx++; |
604 | 0 | } |
605 | 0 | } |
606 | | |
607 | | static void reverse(uint8_t *dest, uint8_t *src, uint16_t size) |
608 | 0 | { |
609 | 0 | for (int i = 0; i < size; i++) |
610 | 0 | { |
611 | 0 | dest[size - i - 1] = src[i]; |
612 | 0 | } |
613 | 0 | } |
614 | | |
615 | | bool decrypt_data( |
616 | | const uint8_t *in, uint8_t *out, |
617 | | uint16_t payload_offset, |
618 | | uint16_t *len, |
619 | | uint8_t src_ieee[RF4CE_IEEE_ADDR_LEN], uint8_t dst_ieee[RF4CE_IEEE_ADDR_LEN]) |
620 | 4 | { |
621 | 4 | bool ret = false; |
622 | 4 | uint8_t frame_control = *in; |
623 | 4 | int idx = 0; |
624 | | |
625 | 4 | if (*len < RF4CE_MIN_NWK_LENGTH || *len > RF4CE_MAX_NWK_LENGTH) |
626 | 0 | { |
627 | 0 | return false; |
628 | 0 | } |
629 | | |
630 | 4 | if (*len < payload_offset + RF4CE_CCM_M) { |
631 | 1 | return false; |
632 | 1 | } |
633 | | |
634 | 195 | while (idx < RF4CE_NWK_KEY_STORAGE_SIZE) |
635 | 192 | { |
636 | 192 | if (nwk_key_storage[idx].is_used) |
637 | 0 | { |
638 | 0 | uint8_t *data_ptr; |
639 | | |
640 | | /* Form the nonce (3.5.11.3 Outgoing frame security) */ |
641 | 0 | uint8_t nonce[RF4CE_IEEE_ADDR_LEN + 4 + 1]; |
642 | |
|
643 | 0 | data_ptr = nonce; |
644 | | /* Source IEEE address */ |
645 | 0 | reverse(data_ptr, src_ieee, RF4CE_IEEE_ADDR_LEN); |
646 | 0 | data_ptr += RF4CE_IEEE_ADDR_LEN; |
647 | | /* Fetch frame counter from the packet (don't check) */ |
648 | 0 | memcpy(data_ptr, in + 1, 4); |
649 | 0 | data_ptr += 4; |
650 | | /* Security level */ |
651 | 0 | *data_ptr = RF4CE_SECUR_CONTROL; |
652 | | |
653 | | /* Form the auth string (3.5.11.3 Outgoing frame security) */ |
654 | 0 | uint8_t auth[1 + 4 + RF4CE_IEEE_ADDR_LEN]; |
655 | |
|
656 | 0 | data_ptr = auth; |
657 | | /* Frame control field */ |
658 | 0 | *data_ptr = frame_control; |
659 | 0 | data_ptr += 1; |
660 | | /* Fetch frame counter from the packet (don't check) */ |
661 | 0 | memcpy(data_ptr, in + 1, 4); |
662 | 0 | data_ptr += 4; |
663 | | /* Destination IEEE address */ |
664 | 0 | reverse(data_ptr, dst_ieee, RF4CE_IEEE_ADDR_LEN); |
665 | |
|
666 | 0 | ret = zbee_sec_ccm_decrypt(nwk_key_storage[idx].nwk_key, |
667 | 0 | nonce, |
668 | 0 | auth, |
669 | 0 | in + payload_offset, |
670 | 0 | out, |
671 | 0 | sizeof(auth), |
672 | 0 | *len - payload_offset - RF4CE_CCM_M, |
673 | 0 | RF4CE_CCM_M); |
674 | |
|
675 | 0 | if (ret) |
676 | 0 | { |
677 | 0 | *len = *len - payload_offset - RF4CE_CCM_M; |
678 | 0 | break; |
679 | 0 | } |
680 | 0 | } |
681 | | |
682 | 192 | idx++; |
683 | 192 | } |
684 | | |
685 | 3 | return ret; |
686 | 4 | } |
687 | | |
688 | | // Calculate the CMAC |
689 | | void rf4ce_aes_cmac(unsigned char *input, unsigned long length, unsigned char *key, unsigned char *mac_value) |
690 | 0 | { |
691 | 0 | gcry_mac_hd_t mac_hd; |
692 | 0 | size_t l = length; |
693 | |
|
694 | 0 | if (gcry_mac_open(&mac_hd, GCRY_MAC_CMAC_AES, 0, NULL)) |
695 | 0 | { |
696 | 0 | return; |
697 | 0 | } |
698 | 0 | if (gcry_mac_setkey(mac_hd, key, KEY_LEN)) |
699 | 0 | { |
700 | 0 | gcry_mac_close(mac_hd); |
701 | 0 | return; |
702 | 0 | } |
703 | 0 | if (gcry_mac_write(mac_hd, input, length)) |
704 | 0 | { |
705 | 0 | gcry_mac_close(mac_hd); |
706 | 0 | return; |
707 | 0 | } |
708 | 0 | if (gcry_mac_read(mac_hd, mac_value, &l)) |
709 | 0 | { |
710 | 0 | gcry_mac_close(mac_hd); |
711 | 0 | return; |
712 | 0 | } |
713 | 0 | gcry_mac_close(mac_hd); |
714 | 0 | } |