### Bug Summary

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

### Annotated Source Code

Press '?' to see keyboard shortcuts

Show analyzer invocation
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-22/lib/clang/22 -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 CARES_NO_DEPRECATED -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-22/lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../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=gnu17 -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 -fdwarf2-cfi-asm -o /builds/wireshark/wireshark/sbout/2026-07-02-100350-3577-1 -x c /builds/wireshark/wireshark/epan/dissectors/packet-knxip_decrypt.c

Keyboard shortcuts:

- Use 'j/k' keys for keyboard navigation
- Use 'Shift+S' to show/hide relevant lines
- Use '?' to toggle this window

Close
Show only relevant lines Show control flow arrows

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