File: | epan/dissectors/packet-knxip_decrypt.c |
Warning: | line 782, column 3 Opened stream never closed. Potential resource leak |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
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)) | |||
| ||||
689 | return; | |||
690 | ||||
691 | root_element = xmlDocGetRootElement(doc); | |||
692 | if (root_element == NULL((void*)0)) | |||
693 | { | |||
694 | xmlFreeDoc(doc); | |||
695 | return; | |||
696 | } | |||
697 | ||||
698 | /* Find the Keyring element */ | |||
699 | if (xmlStrcmp(root_element->name, (const xmlChar*)"Keyring") == 0) | |||
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
| |||
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) : | |||
722 | (strcmp( key_info_file, "-" ) == 0) ? stdoutstdout : | |||
723 | ws_fopenfopen( key_info_file, "w" ); | |||
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) | |||
729 | { | |||
730 | if (xmlStrcmp(attr->name, (const xmlChar*)"Created") == 0) | |||
731 | { | |||
732 | xmlChar* str_created = xmlNodeListGetString(key_ring->doc, attr->children, 1); | |||
733 | if (str_created != NULL((void*)0)) | |||
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) | |||
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
| |||
781 | fclose(f2); | |||
782 | xmlFreeDoc(doc); | |||
| ||||
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 | */ |