### Bug Summary

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