Bug Summary

File:builds/wireshark/wireshark/epan/dissectors/packet-knxip_decrypt.c
Warning:line 810, column 3
Opened stream never closed. Potential resource leak

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name packet-knxip_decrypt.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-21/lib/clang/21 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /builds/wireshark/wireshark/epan/dissectors -isystem /builds/wireshark/wireshark/build/epan/dissectors -isystem /usr/include/mit-krb5 -isystem /usr/include/libxml2 -isystem /builds/wireshark/wireshark/epan -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_BUILD_DLL -D WS_DEBUG -D WS_DEBUG_UTF_8 -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-nonliteral -std=gnu11 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2026-01-28-100410-3623-1 -x c /builds/wireshark/wireshark/epan/dissectors/packet-knxip_decrypt.c
1/* packet-knxip_decrypt.c
2 * Decryption keys and decryption functions for KNX/IP Dissector
3 * Copyright 2018, ise GmbH <[email protected]>
4 *
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <[email protected]>
7 * Copyright 1998 Gerald Combs
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12#include "config.h"
13
14#define WS_LOG_DOMAIN"packet-knxip" "packet-knxip"
15
16#include <wsutil/file_util.h>
17#include <epan/proto.h>
18#include "packet-knxip_decrypt.h"
19#include <epan/wmem_scopes.h>
20#include <wsutil/wsgcrypt.h>
21#include <wsutil/strtoi.h>
22#include <wsutil/wslog.h>
23#include <wsutil/inet_addr.h>
24#include <libxml/tree.h>
25#include <libxml/parser.h>
26#include <libxml/xpath.h>
27
28#define TEXT_BUFFER_SIZE128 128
29
30#define IPA_SIZE4 4 // = size of IPv4 address
31
32#define BASE64_KNX_KEY_LENGTH24 24 // = length of base64 encoded KNX key
33
34struct knx_keyring_mca_keys* knx_keyring_mca_keys;
35struct knx_keyring_ga_keys* knx_keyring_ga_keys;
36struct knx_keyring_ga_senders* knx_keyring_ga_senders;
37struct knx_keyring_ia_keys* knx_keyring_ia_keys;
38struct knx_keyring_ia_seqs* knx_keyring_ia_seqs;
39
40// Encrypt 16-byte block via AES
41static bool_Bool encrypt_block( const uint8_t key[ KNX_KEY_LENGTH16 ], const uint8_t plain[ KNX_KEY_LENGTH16 ], uint8_t p_crypt[ KNX_KEY_LENGTH16 ] )
42{
43 gcry_cipher_hd_t cryptor = NULL((void*)0);
44 gcry_error_t err;
45 err = gcry_cipher_open( &cryptor, GCRY_CIPHER_AES128GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0 );
46 if (err != 0) {
47 ws_debug("failed to open AES128 cipher handle: %s/%s", gcry_strsource(err), gcry_strerror(err))do { if (1) { ws_log_full("packet-knxip", LOG_LEVEL_DEBUG, "epan/dissectors/packet-knxip_decrypt.c"
, 47, __func__, "failed to open AES128 cipher handle: %s/%s",
gcry_strsource(err), gcry_strerror(err)); } } while (0)
;
48 return false0;
49 }
50 err = gcry_cipher_setkey( cryptor, key, KNX_KEY_LENGTH16 );
51 if (err != 0) {
52 ws_debug("failed to set AES128 cipher key: %s/%s", gcry_strsource(err), gcry_strerror(err))do { if (1) { ws_log_full("packet-knxip", LOG_LEVEL_DEBUG, "epan/dissectors/packet-knxip_decrypt.c"
, 52, __func__, "failed to set AES128 cipher key: %s/%s", gcry_strsource
(err), gcry_strerror(err)); } } while (0)
;
53 gcry_cipher_close( cryptor );
54 return false0;
55 }
56 err = gcry_cipher_encrypt( cryptor, p_crypt, KNX_KEY_LENGTH16, plain, KNX_KEY_LENGTH16 );
57 if (err != 0) {
58 ws_debug("failed to encrypt AES128: %s/%s", gcry_strsource(err), gcry_strerror(err))do { if (1) { ws_log_full("packet-knxip", LOG_LEVEL_DEBUG, "epan/dissectors/packet-knxip_decrypt.c"
, 58, __func__, "failed to encrypt AES128: %s/%s", gcry_strsource
(err), gcry_strerror(err)); } } while (0)
;
59 gcry_cipher_close( cryptor );
60 return false0;
61 }
62 gcry_cipher_close( cryptor );
63 return true1;
64}
65
66// Create B_0 for CBC-MAC
67static void build_b0( uint8_t p_result[ KNX_KEY_LENGTH16 ], const uint8_t* nonce, uint8_t nonce_length )
68{
69 DISSECTOR_ASSERT( nonce_length <= KNX_KEY_LENGTH )((void) ((nonce_length <= 16) ? (void)0 : (proto_report_dissector_bug
("%s:%u: failed assertion \"%s\"", "epan/dissectors/packet-knxip_decrypt.c"
, 69, "nonce_length <= 16"))))
;
70 if( nonce_length ) memcpy( p_result, nonce, nonce_length );
71 memset( p_result + nonce_length, 0, KNX_KEY_LENGTH16 - nonce_length );
72}
73
74// Create Ctr_0 for CCM encryption/decryption
75static void build_ctr0( uint8_t p_result[ KNX_KEY_LENGTH16 ], const uint8_t* nonce, uint8_t nonce_length )
76{
77 build_b0( p_result, nonce, nonce_length );
78 p_result[ KNX_KEY_LENGTH16 - 2 ] = 0xFF;
79}
80
81// Calculate MAC for KNX IP Security or KNX Data Security
82void knx_ccm_calc_cbc_mac(uint8_t p_mac[ KNX_KEY_LENGTH16 ], const uint8_t key[ KNX_KEY_LENGTH16 ],
83 const uint8_t* a_bytes, int a_length, const uint8_t* p_bytes, int p_length,
84 const uint8_t b_0[ KNX_KEY_LENGTH16 ] )
85{
86 uint8_t plain[ KNX_KEY_LENGTH16 ];
87 uint8_t b_pos;
88
89 // Add B_0
90 memcpy( plain, b_0, KNX_KEY_LENGTH16 );
91 if (!encrypt_block( key, plain, p_mac )) {
92 memset(p_mac, 0, KNX_KEY_LENGTH16);
93 return;
94 }
95
96 // Add a_length
97 plain[ 0 ] = (uint8_t) ((a_length >> 8) ^ p_mac[ 0 ]);
98 plain[ 1 ] = (uint8_t) ((a_length & 0xFF) ^ p_mac[ 1 ]);
99 b_pos = 2;
100
101 // Add a_bytes directly followed by p_bytes
102 while( a_length || p_length )
103 {
104 while( a_length && b_pos < KNX_KEY_LENGTH16 )
105 {
106 plain[ b_pos ] = *a_bytes++ ^ p_mac[ b_pos ];
107 --a_length;
108 ++b_pos;
109 }
110
111 while( p_length && b_pos < KNX_KEY_LENGTH16 )
112 {
113 plain[ b_pos ] = *p_bytes++ ^ p_mac[ b_pos ];
114 --p_length;
115 ++b_pos;
116 }
117
118 while( b_pos < KNX_KEY_LENGTH16 )
119 {
120 plain[ b_pos ] = p_mac[ b_pos ];
121 ++b_pos;
122 }
123
124 if (!encrypt_block( key, plain, p_mac )) {
125 memset(p_mac, 0, KNX_KEY_LENGTH16);
126 return;
127 }
128
129 b_pos = 0;
130 }
131}
132
133// Calculate MAC for KNX IP Security, using 6-byte Sequence ID
134void knxip_ccm_calc_cbc_mac( uint8_t p_mac[ KNX_KEY_LENGTH16 ], const uint8_t key[ KNX_KEY_LENGTH16 ],
135 const uint8_t* a_bytes, int a_length, const uint8_t* p_bytes, int p_length,
136 const uint8_t* nonce, uint8_t nonce_length )
137{
138 uint8_t b_0[ KNX_KEY_LENGTH16 ];
139 build_b0( b_0, nonce, nonce_length );
140 b_0[ KNX_KEY_LENGTH16 - 2 ] = (uint8_t) (p_length >> 8);
141 b_0[ KNX_KEY_LENGTH16 - 1 ] = (uint8_t) (p_length & 0xFF);
142 knx_ccm_calc_cbc_mac( p_mac, key, a_bytes, a_length, p_bytes, p_length, b_0 );
143}
144
145// Encrypt for KNX IP Security or KNX Data Security
146uint8_t* knx_ccm_encrypt(wmem_allocator_t* scope, uint8_t* p_result, const uint8_t key[ KNX_KEY_LENGTH16 ], const uint8_t* p_bytes, int p_length,
147 const uint8_t* mac, uint8_t mac_length, const uint8_t ctr_0[ KNX_KEY_LENGTH16 ], uint8_t s0_bytes_used_for_mac )
148{
149 if( p_length >= 0 && !(p_length && !p_bytes) )
150 {
151 // NB: mac_length = 16 (for IP Security), or 4 (for Data Security)
152
153 uint8_t* result = p_result ? p_result : (uint8_t*) wmem_alloc(scope, p_length + mac_length );
154
155 uint8_t* dest = result;
156
157 uint8_t ctr[ KNX_KEY_LENGTH16 ];
158 uint8_t mask[ KNX_KEY_LENGTH16 ];
159 uint8_t mask_0[ KNX_KEY_LENGTH16 ];
160 uint8_t b_pos;
161
162 // Encrypt ctr_0 for mac
163 memcpy( ctr, ctr_0, KNX_KEY_LENGTH16 );
164 if (!encrypt_block( key, ctr, mask_0 )) {
165 if (!p_result) wmem_free(scope, result);
166 return NULL((void*)0);
167 }
168
169 // Encrypt p_bytes with rest of S_0, only if mac_length < 16.
170 b_pos = s0_bytes_used_for_mac;
171 while (p_length && b_pos < KNX_KEY_LENGTH16 )
172 {
173 *dest++ = mask_0[b_pos++] ^ *p_bytes++;
174 --p_length;
175 }
176
177 // Encrypt p_bytes
178 while( p_length )
179 {
180 // Increment and encrypt ctr
181 ++ctr[ KNX_KEY_LENGTH16 - 1 ];
182 if (!encrypt_block( key, ctr, mask )) {
183 if (!p_result) wmem_free(scope, result);
184 return NULL((void*)0);
185 }
186
187 // Encrypt input block via encrypted ctr
188 b_pos = 0;
189 while( p_length && b_pos < KNX_KEY_LENGTH16 )
190 {
191 *dest++ = mask[ b_pos++] ^ *p_bytes++;
192 --p_length;
193 }
194 }
195
196 if( mac )
197 {
198 if( mac_length > KNX_KEY_LENGTH16 )
199 {
200 mac_length = KNX_KEY_LENGTH16;
201 }
202
203 // Encrypt and append mac
204 b_pos = 0;
205 while( mac_length )
206 {
207 *dest++ = mask_0[ b_pos++] ^ *mac++;
208 --mac_length;
209 }
210 }
211
212 return result;
213 }
214
215 return NULL((void*)0);
216}
217
218// Encrypt for KNX IP Security (with 16-byte MAC and Nonce based on 6-byte Sequence ID)
219uint8_t* knxip_ccm_encrypt(wmem_allocator_t* scope, uint8_t* p_result, const uint8_t key[ KNX_KEY_LENGTH16 ], const uint8_t* p_bytes, int p_length,
220 const uint8_t mac[KNX_KEY_LENGTH16], const uint8_t* nonce, uint8_t nonce_length )
221{
222 uint8_t ctr_0[ KNX_KEY_LENGTH16 ];
223 build_ctr0( ctr_0, nonce, nonce_length );
224 return knx_ccm_encrypt(scope, p_result, key, p_bytes, p_length, mac, KNX_KEY_LENGTH16, ctr_0, KNX_KEY_LENGTH16 );
225}
226
227// Decrypt for KNX-IP Security (with 16-byte MAC and Nonce based on 6-byte Sequence ID)
228uint8_t* knxip_ccm_decrypt(wmem_allocator_t* scope, uint8_t* p_result, const uint8_t key[ KNX_KEY_LENGTH16 ], const uint8_t* crypt, int crypt_length,
229 const uint8_t* nonce, uint8_t nonce_length )
230{
231 int p_length = crypt_length - KNX_KEY_LENGTH16;
232 uint8_t ctr_0[ KNX_KEY_LENGTH16 ];
233 build_ctr0( ctr_0, nonce, nonce_length );
234 return knx_ccm_encrypt(scope, p_result, key, crypt, p_length, crypt + p_length, KNX_KEY_LENGTH16, ctr_0, KNX_KEY_LENGTH16 );
235}
236
237static void fprintf_hex( FILE* f, const uint8_t* data, uint8_t length )
238{
239 for( ; length; --length ) fprintf( f, " %02X", *data++ );
240 fputc( '\n', f );
241}
242
243static void clear_keyring_data( void )
244{
245 while( knx_keyring_mca_keys )
246 {
247 struct knx_keyring_mca_keys* mca_key = knx_keyring_mca_keys;
248 knx_keyring_mca_keys = mca_key->next;
249 wmem_free( wmem_epan_scope(), mca_key );
250 }
251
252 while( knx_keyring_ga_keys )
253 {
254 struct knx_keyring_ga_keys* ga_key = knx_keyring_ga_keys;
255 knx_keyring_ga_keys = ga_key->next;
256 wmem_free( wmem_epan_scope(), ga_key );
257 }
258
259 while( knx_keyring_ga_senders )
260 {
261 struct knx_keyring_ga_senders* ga_sender = knx_keyring_ga_senders;
262 knx_keyring_ga_senders = ga_sender->next;
263 wmem_free( wmem_epan_scope(), ga_sender );
264 }
265
266 while( knx_keyring_ia_keys )
267 {
268 struct knx_keyring_ia_keys* ia_key = knx_keyring_ia_keys;
269 knx_keyring_ia_keys = ia_key->next;
270 wmem_free( wmem_epan_scope(), ia_key );
271 }
272
273 while( knx_keyring_ia_seqs )
274 {
275 struct knx_keyring_ia_seqs* ia_seq = knx_keyring_ia_seqs;
276 knx_keyring_ia_seqs = ia_seq->next;
277 wmem_free( wmem_epan_scope(), ia_seq );
278 }
279}
280
281// Read IP address
282static void read_ip_addr( uint8_t result[ 4 ], const char* text )
283{
284 ws_in4_addr value = 0;
285 if( ws_inet_pton4( text, &value ) )
286 memcpy( result, &value, 4 );
287 else
288 memset( result, 0, 4 );
289}
290
291// Read KNX group address
292static uint16_t read_ga( const char* text )
293{
294 unsigned a[ 3 ];
295 int n = sscanf( text, "%u/%u/%u", a, a + 1, a + 2 );
296 return
297 (n == 1) ? (uint16_t) a[ 0 ] :
298 (n == 2) ? (uint16_t) ((a[ 0 ] << 11) | a[ 1 ]) :
299 (n == 3) ? (uint16_t) ((a[ 0 ] << 11) | (a[ 1 ] << 8) | a[ 2 ]) :
300 0;
301}
302
303// Read KNX individual address
304static uint16_t read_ia( const char* text )
305{
306 unsigned a[ 3 ];
307 int n = sscanf( text, "%u.%u.%u", a, a + 1, a + 2 );
308 return
309 (n == 1) ? (uint16_t) a[ 0 ] :
310 (n == 2) ? (uint16_t) ((a[ 0 ] << 8) | a[ 1 ]) :
311 (n == 3) ? (uint16_t) ((a[ 0 ] << 12) | (a[ 1 ] << 8) | a[ 2 ]) :
312 0;
313}
314
315// Read 6-byte sequence number from decimal representation
316static uint64_t read_seq( const char* text )
317{
318 uint64_t result;
319 return ws_strtou64( text, NULL((void*)0), &result ) ? result : 0;
320}
321
322// Decrypt key
323static void decrypt_key( uint8_t key[] _U___attribute__((unused)), uint8_t password_hash[] _U___attribute__((unused)), uint8_t created_hash[] _U___attribute__((unused)) )
324{
325 // TODO: decrypt as AES128-CBC(key, password_hash, created_hash)
326}
327
328// Decode and decrypt key
329static void decode_and_decrypt_key( uint8_t key[ BASE64_KNX_KEY_LENGTH24 + 1 ], const char* text, uint8_t password_hash[], uint8_t created_hash[] )
330{
331 size_t out_len;
332 snprintf( (char*) key, BASE64_KNX_KEY_LENGTH24 + 1, "%s", text );
333 g_base64_decode_inplace( (char*) key, &out_len );
334 decrypt_key( key, password_hash, created_hash );
335}
336
337// Add MCA <-> key association
338static void add_mca_key( const uint8_t mca[ IPA_SIZE4 ], const char* text, uint8_t password_hash[], uint8_t created_hash[], FILE* f2 )
339{
340 int text_length = (int) strlen( text );
341
342 if( text_length == BASE64_KNX_KEY_LENGTH24 )
343 {
344 uint8_t key[ BASE64_KNX_KEY_LENGTH24 + 1 ];
345 struct knx_keyring_mca_keys** mca_keys_next;
346 struct knx_keyring_mca_keys* mca_key;
347
348 decode_and_decrypt_key( key, text, password_hash, created_hash );
349
350 mca_keys_next = &knx_keyring_mca_keys;
351
352 while( (mca_key = *mca_keys_next) != NULL((void*)0) )
353 {
354 if( memcmp( mca_key->mca, mca, IPA_SIZE4 ) == 0 )
355 {
356 if( memcmp( mca_key->key, key, KNX_KEY_LENGTH16 ) == 0 )
357 {
358 return;
359 }
360 }
361
362 mca_keys_next = &mca_key->next;
363 }
364
365 if( f2 )
366 {
367 fprintf( f2, "MCA %u.%u.%u.%u key", mca[ 0 ], mca[ 1 ], mca[ 2 ], mca[ 3 ] );
368 fprintf_hex( f2, key, KNX_KEY_LENGTH16 );
369 }
370
371 mca_key = wmem_new(wmem_epan_scope(), struct knx_keyring_mca_keys)((struct knx_keyring_mca_keys*)wmem_alloc((wmem_epan_scope())
, sizeof(struct knx_keyring_mca_keys)))
;
372
373 if( mca_key )
374 {
375 mca_key->next = NULL((void*)0);
376 memcpy( mca_key->mca, mca, IPA_SIZE4 );
377 memcpy( mca_key->key, key, KNX_KEY_LENGTH16 );
378
379 *mca_keys_next = mca_key;
380 }
381 }
382}
383
384// Add GA <-> key association
385static void add_ga_key( uint16_t ga, const char* text, uint8_t password_hash[], uint8_t created_hash[], FILE* f2 )
386{
387 int text_length = (int) strlen( text );
388
389 if( text_length == BASE64_KNX_KEY_LENGTH24 )
390 {
391 uint8_t key[ BASE64_KNX_KEY_LENGTH24 + 1 ];
392 struct knx_keyring_ga_keys** ga_keys_next;
393 struct knx_keyring_ga_keys* ga_key;
394
395 decode_and_decrypt_key( key, text, password_hash, created_hash );
396
397 ga_keys_next = &knx_keyring_ga_keys;
398
399 while( (ga_key = *ga_keys_next) != NULL((void*)0) )
400 {
401 if( ga_key->ga == ga )
402 {
403 if( memcmp( ga_key->key, key, KNX_KEY_LENGTH16 ) == 0 )
404 {
405 return;
406 }
407 }
408
409 ga_keys_next = &ga_key->next;
410 }
411
412 if( f2 )
413 {
414 fprintf( f2, "GA %d/%d/%d key", (ga >> 11) & 0x1F, (ga >> 8) & 0x7, ga & 0xFF );
415 fprintf_hex( f2, key, KNX_KEY_LENGTH16 );
416 }
417
418 ga_key = wmem_new(wmem_epan_scope(), struct knx_keyring_ga_keys)((struct knx_keyring_ga_keys*)wmem_alloc((wmem_epan_scope()),
sizeof(struct knx_keyring_ga_keys)))
;
419
420 if( ga_key )
421 {
422 ga_key->next = NULL((void*)0);
423 ga_key->ga = ga;
424 memcpy( ga_key->key, key, KNX_KEY_LENGTH16 );
425
426 *ga_keys_next = ga_key;
427 }
428 }
429}
430
431// Add GA <-> sender association
432static void add_ga_sender( uint16_t ga, const char* text, FILE* f2 )
433{
434 uint16_t ia = read_ia( text );
435 struct knx_keyring_ga_senders** ga_senders_next = &knx_keyring_ga_senders;
436 struct knx_keyring_ga_senders* ga_sender;
437
438 while( (ga_sender = *ga_senders_next) != NULL((void*)0) )
439 {
440 if( ga_sender->ga == ga )
441 {
442 if( ga_sender->ia == ia )
443 {
444 return;
445 }
446 }
447
448 ga_senders_next = &ga_sender->next;
449 }
450
451 if( f2 )
452 {
453 fprintf( f2, "GA %d/%d/%d sender %d.%d.%d\n", (ga >> 11) & 0x1F, (ga >> 8) & 0x7, ga & 0xFF, (ia >> 12) & 0xF, (ia >> 8) & 0xF, ia & 0xFF );
454 }
455
456 ga_sender = wmem_new(wmem_epan_scope(), struct knx_keyring_ga_senders)((struct knx_keyring_ga_senders*)wmem_alloc((wmem_epan_scope(
)), sizeof(struct knx_keyring_ga_senders)))
;
457
458 if( ga_sender )
459 {
460 ga_sender->next = NULL((void*)0);
461 ga_sender->ga = ga;
462 ga_sender->ia = ia;
463
464 *ga_senders_next = ga_sender;
465 }
466}
467
468// Add IA <-> key association
469static void add_ia_key( uint16_t ia, const char* text, uint8_t password_hash[], uint8_t created_hash[], FILE* f2 )
470{
471 int text_length = (int) strlen( text );
472
473 if( text_length == BASE64_KNX_KEY_LENGTH24 )
474 {
475 uint8_t key[ BASE64_KNX_KEY_LENGTH24 + 1 ];
476 struct knx_keyring_ia_keys** ia_keys_next;
477 struct knx_keyring_ia_keys* ia_key;
478
479 decode_and_decrypt_key( key, text, password_hash, created_hash );
480
481 ia_keys_next = &knx_keyring_ia_keys;
482
483 while( (ia_key = *ia_keys_next) != NULL((void*)0) )
484 {
485 if( ia_key->ia == ia )
486 {
487 if( memcmp( ia_key->key, key, KNX_KEY_LENGTH16 ) == 0 )
488 {
489 return;
490 }
491 }
492
493 ia_keys_next = &ia_key->next;
494 }
495
496 if( f2 )
497 {
498 fprintf( f2, "IA %d.%d.%d key", (ia >> 12) & 0xF, (ia >> 8) & 0xF, ia & 0xFF );
499 fprintf_hex( f2, key, KNX_KEY_LENGTH16 );
500 }
501
502 ia_key = wmem_new(wmem_epan_scope(), struct knx_keyring_ia_keys)((struct knx_keyring_ia_keys*)wmem_alloc((wmem_epan_scope()),
sizeof(struct knx_keyring_ia_keys)))
;
503
504 if( ia_key )
505 {
506 ia_key->next = NULL((void*)0);
507 ia_key->ia = ia;
508 memcpy( ia_key->key, key, KNX_KEY_LENGTH16 );
509
510 *ia_keys_next = ia_key;
511 }
512 }
513}
514
515// Add IA <-> sequence number association
516static void add_ia_seq( uint16_t ia, const char* text, FILE* f2 )
517{
518 uint64_t seq = read_seq( text );
519
520 struct knx_keyring_ia_seqs** ia_seqs_next = &knx_keyring_ia_seqs;
521 struct knx_keyring_ia_seqs* ia_seq;
522
523 while( (ia_seq = *ia_seqs_next) != NULL((void*)0) )
524 {
525 if( ia_seq->ia == ia )
526 {
527 if( ia_seq->seq == seq )
528 {
529 return;
530 }
531 }
532
533 ia_seqs_next = &ia_seq->next;
534 }
535
536 if( f2 )
537 {
538 fprintf( f2, "IA %u.%u.%u SeqNr %" PRIu64"l" "u" "\n", (ia >> 12) & 0xF, (ia >> 8) & 0xF, ia & 0xFF, seq );
539 }
540
541 ia_seq = wmem_new(wmem_epan_scope(), struct knx_keyring_ia_seqs)((struct knx_keyring_ia_seqs*)wmem_alloc((wmem_epan_scope()),
sizeof(struct knx_keyring_ia_seqs)))
;
542
543 if( ia_seq )
544 {
545 ia_seq->next = NULL((void*)0);
546 ia_seq->ia = ia;
547 ia_seq->seq = seq;
548
549 *ia_seqs_next = ia_seq;
550 }
551}
552
553// Calculate PBKDF2(HMAC-SHA256, password, "1.keyring.ets.knx.org", 65536, 128)
554static void make_password_hash( uint8_t password_hash[] _U___attribute__((unused)), const char* password _U___attribute__((unused)) )
555{
556 // TODO: password_hash = PBKDF2(HMAC-SHA256, password, "1.keyring.ets.knx.org", 65536, 128)
557}
558
559// Calculate MSB128(SHA256(created))
560static void make_created_hash( uint8_t created_hash[] _U___attribute__((unused)), const char* created _U___attribute__((unused)) )
561{
562 // TODO: created_hash = MSB128(SHA256(created))
563}
564
565static void read_knx_keyring_xml_backbone_element(xmlNodePtr backbone, uint8_t password_hash[], uint8_t created_hash[], FILE* f2)
566{
567 bool_Bool address_valid = false0;
568 uint8_t multicast_address[IPA_SIZE4] = { 0 };
569
570 /* Parse out the attributes of the Backbone element */
571 for (xmlAttrPtr attr = backbone->properties; attr; attr = attr->next)
572 {
573 if (xmlStrcmp(attr->name, (const xmlChar*)"MulticastAddress") == 0)
574 {
575 xmlChar* str_address = xmlNodeListGetString(backbone->doc, attr->children, 1);
576 if (str_address != NULL((void*)0))
577 {
578 read_ip_addr(multicast_address, (const char*)str_address);
579 address_valid = true1;
580 xmlFree(str_address);
581 }
582 }
583 else if (xmlStrcmp(attr->name, (const xmlChar*)"Key") == 0)
584 {
585 if (address_valid)
586 {
587 xmlChar* str_key = xmlNodeListGetString(backbone->doc, attr->children, 1);
588 if (str_key != NULL((void*)0))
589 {
590 add_mca_key(multicast_address, (const char*)str_key, password_hash, created_hash, f2);
591 xmlFree(str_key);
592 }
593 }
594 }
595 }
596
597}
598
599static void read_knx_keyring_xml_group_element(xmlNodePtr group, uint8_t password_hash[], uint8_t created_hash[], FILE* f2)
600{
601 bool_Bool address_valid = false0;
602 uint16_t addr = 0;
603
604 /* Parse out the attributes of the Group element */
605 for (xmlAttrPtr attr = group->properties; attr; attr = attr->next)
606 {
607 if (xmlStrcmp(attr->name, (const xmlChar*)"Address") == 0)
608 {
609 xmlChar* str_address = xmlNodeListGetString(group->doc, attr->children, 1);
610 if (str_address != NULL((void*)0))
611 {
612 addr = read_ga((const char*)str_address);
613 address_valid = true1;
614 xmlFree(str_address);
615 }
616 }
617 else if (xmlStrcmp(attr->name, (const xmlChar*)"Key") == 0)
618 {
619 if (address_valid)
620 {
621 xmlChar* str_key = xmlNodeListGetString(group->doc, attr->children, 1);
622 add_ga_key(addr, (const char*)str_key, password_hash, created_hash, f2);
623 xmlFree(str_key);
624 }
625 }
626 else if (xmlStrcmp(attr->name, (const xmlChar*)"Senders") == 0)
627 {
628 if (address_valid)
629 {
630 xmlChar* str_senders = xmlNodeListGetString(group->doc, attr->children, 1);
631 if (str_senders != NULL((void*)0))
632 {
633 // Add senders given by space separated list of KNX IAs
634 static const char delim[] = " ,";
635 const char* token = strtok((char*)str_senders, delim);
636 while (token)
637 {
638 add_ga_sender(addr, token, f2);
639 token = strtok(NULL((void*)0), delim);
640 }
641 xmlFree(str_senders);
642 }
643 }
644 }
645 }
646
647}
648
649static void read_knx_keyring_xml_device_element(xmlNodePtr device, uint8_t password_hash[], uint8_t created_hash[], FILE* f2)
650{
651 bool_Bool address_valid = false0;
652 uint16_t addr = 0;
653
654 /* Parse out the attributes of the Device element */
655 for (xmlAttrPtr attr = device->properties; attr; attr = attr->next)
656 {
657 if (xmlStrcmp(attr->name, (const xmlChar*)"IndividualAddress") == 0)
658 {
659 xmlChar* str_address = xmlNodeListGetString(device->doc, attr->children, 1);
660 if (str_address != NULL((void*)0))
661 {
662 addr = read_ia((const char*)str_address);
663 address_valid = true1;
664 xmlFree(str_address);
665 }
666 }
667 else if (xmlStrcmp(attr->name, (const xmlChar*)"ToolKey") == 0)
668 {
669 if (address_valid)
670 {
671 xmlChar* str_key = xmlNodeListGetString(device->doc, attr->children, 1);
672 if (str_key != NULL((void*)0))
673 {
674 add_ia_key(addr, (const char*)str_key, password_hash, created_hash, f2);
675 xmlFree(str_key);
676 }
677 }
678 }
679 else if (xmlStrcmp(attr->name, (const xmlChar*)"SequenceNumber") == 0)
680 {
681 if (address_valid)
682 {
683 xmlChar* str_seq = xmlNodeListGetString(device->doc, attr->children, 1);
684 if (str_seq != NULL((void*)0))
685 {
686 add_ia_seq(addr, (const char*)str_seq, f2);
687 xmlFree(str_seq);
688 }
689 }
690 }
691 }
692}
693
694// Read KNX security key info from keyring XML file.
695//
696// An example keyring XML file is
697// "test/keys/knx_keyring.xml".
698//
699// Corresponding test is
700// suite_decryption.case_decrypt_knxip.test_knxip_keyring_xml_import
701//
702// Resulting decoded and decrypted 16-byte keys with context info are optionally written to a "key info" text file.
703// This may be useful, as these keys are not directly available from the keyring XML file .
704void read_knx_keyring_xml_file(const char* key_file, const char* password, const char* key_info_file)
705{
706 xmlDocPtr doc;
707 xmlNodePtr root_element = NULL((void*)0);
708 xmlNodePtr key_ring = NULL((void*)0);
709 uint8_t password_hash[KNX_KEY_LENGTH16] = { 0 };
710 uint8_t created_hash[KNX_KEY_LENGTH16] = {0};
711
712 // Clear old keyring data
713 clear_keyring_data();
714
715 doc = xmlReadFile(key_file, NULL((void*)0), 0);
716 if (doc == NULL((void*)0))
1
Assuming 'doc' is not equal to NULL
2
Taking false branch
717 return;
718
719 root_element = xmlDocGetRootElement(doc);
720 if (root_element == NULL((void*)0))
3
Assuming 'root_element' is not equal to NULL
4
Taking false branch
721 {
722 xmlFreeDoc(doc);
723 return;
724 }
725
726 /* Find the Keyring element */
727 if (xmlStrcmp(root_element->name, (const xmlChar*)"Keyring") == 0)
5
Assuming the condition is true
6
Taking true branch
728 {
729 key_ring = root_element;
730 }
731 else
732 {
733 for (xmlNodePtr cur = root_element->children; cur != NULL((void*)0); cur = cur->next)
734 {
735 if (cur->type == XML_ELEMENT_NODE && xmlStrcmp(cur->name, (const xmlChar*)"Keyring") == 0)
736 {
737 key_ring = cur;
738 break;
739 }
740 }
741 }
742
743 if (key_ring
6.1
'key_ring' is not equal to NULL
== NULL((void*)0)) {
744 xmlFreeDoc(doc);
745 return;
746 }
747
748 // Optionally write extracted data to key info file
749 FILE* f2 = (!key_info_file || !*key_info_file) ? NULL((void*)0) :
7
Assuming 'key_info_file' is non-null
8
Assuming the condition is false
750 (strcmp( key_info_file, "-" ) == 0) ? stdoutstdout :
9
Assuming the condition is false
10
'?' condition is false
751 ws_fopenfopen( key_info_file, "w" );
11
Stream opened here
12
Assuming that 'fopen' is successful
752
753 make_password_hash(password_hash, password);
754
755 /* Parse out the attributes of the Keyring element */
756 for (xmlAttrPtr attr = key_ring->properties; attr; attr = attr->next)
13
Loop condition is true. Entering loop body
18
Loop condition is false. Execution continues on line 770
757 {
758 if (xmlStrcmp(attr->name, (const xmlChar*)"Created") == 0)
14
Assuming the condition is true
15
Taking true branch
759 {
760 xmlChar* str_created = xmlNodeListGetString(key_ring->doc, attr->children, 1);
761 if (str_created != NULL((void*)0))
16
Assuming 'str_created' is not equal to NULL
17
Taking true branch
762 {
763 make_created_hash(created_hash, (const char*)str_created);
764 xmlFree(str_created);
765 }
766 }
767 }
768
769 /* Parse out subelements of Keyring element */
770 for (xmlNodePtr cur = key_ring->children; cur != NULL((void*)0); cur = cur->next)
19
Assuming 'cur' is equal to NULL
771 {
772 if (cur->type == XML_ELEMENT_NODE && xmlStrcmp(cur->name, (const xmlChar*)"Backbone") == 0)
773 {
774 read_knx_keyring_xml_backbone_element(cur, password_hash, created_hash, f2);
775 }
776 else if (cur->type == XML_ELEMENT_NODE && xmlStrcmp(cur->name, (const xmlChar*)"Interface") == 0)
777 {
778 for (xmlNodePtr group = cur->children; group != NULL((void*)0); group = group->next)
779 {
780 if (group->type == XML_ELEMENT_NODE && xmlStrcmp(group->name, (const xmlChar*)"Group") == 0)
781 {
782 read_knx_keyring_xml_group_element(group, password_hash, created_hash, f2);
783 }
784 }
785 }
786 else if (cur->type == XML_ELEMENT_NODE && xmlStrcmp(cur->name, (const xmlChar*)"GroupAddresses") == 0)
787 {
788 for (xmlNodePtr group = cur->children; group != NULL((void*)0); group = group->next)
789 {
790 if (group->type == XML_ELEMENT_NODE && xmlStrcmp(group->name, (const xmlChar*)"Group") == 0)
791 {
792 read_knx_keyring_xml_group_element(group, password_hash, created_hash, f2);
793 }
794 }
795 }
796 else if (cur->type == XML_ELEMENT_NODE && xmlStrcmp(cur->name, (const xmlChar*)"Devices") == 0)
797 {
798 for (xmlNodePtr device = cur->children; device != NULL((void*)0); device = device->next)
799 {
800 if (device->type == XML_ELEMENT_NODE && xmlStrcmp(device->name, (const xmlChar*)"Device") == 0)
801 {
802 read_knx_keyring_xml_device_element(device, password_hash, created_hash, f2);
803 }
804 }
805 }
806 }
807
808 if (f2
19.1
'f2' is non-null
&& f2 != stdoutstdout)
20
Assuming 'f2' is equal to 'stdout'
21
Taking false branch
809 fclose(f2);
810 xmlFreeDoc(doc);
22
Opened stream never closed. Potential resource leak
811}
812
813/*
814 * Editor modelines - https://www.wireshark.org/tools/modelines.html
815 *
816 * Local variables:
817 * c-basic-offset: 2
818 * tab-width: 8
819 * indent-tabs-mode: nil
820 * End:
821 *
822 * vi: set shiftwidth=2 tabstop=8 expandtab:
823 * :indentSize=2:tabSize=8:noTabs=true:
824 */