diff options
author | Anders Broman <anders.broman@ericsson.com> | 2010-10-24 10:04:29 +0000 |
---|---|---|
committer | Anders Broman <anders.broman@ericsson.com> | 2010-10-24 10:04:29 +0000 |
commit | 43cb273d08e3d2e9f5526adcd9acf7cce2dd88f8 (patch) | |
tree | d6365afbff5e656a1e54da04c4945873eedd51fb /epan/dissectors/packet-zbee-security.c | |
parent | dbd143d68a42c17e69c0f356427c7256088eac75 (diff) |
From Fred Fierling:
Multi-key Support and Extended Address Mapping for ZigBee Dissectors
https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5331
svn path=/trunk/; revision=34627
Diffstat (limited to 'epan/dissectors/packet-zbee-security.c')
-rw-r--r-- | epan/dissectors/packet-zbee-security.c | 715 |
1 files changed, 466 insertions, 249 deletions
diff --git a/epan/dissectors/packet-zbee-security.c b/epan/dissectors/packet-zbee-security.c index 2bde0c9b81..246dfcf445 100644 --- a/epan/dissectors/packet-zbee-security.c +++ b/epan/dissectors/packet-zbee-security.c @@ -1,6 +1,6 @@ /* packet-zbee-security.c * Dissector helper routines for encrypted ZigBee frames. - * By Owen Kirby <osk@exegin.com> + * By Owen Kirby <osk@exegin.com>; portions by Fred Fierling <fff@exegin.com> * Copyright 2009 Exegin Technologies Limited * * $Id$ @@ -38,6 +38,8 @@ #include <epan/prefs.h> #include <epan/expert.h> +#include <epan/uat.h> + /* We require libgcrpyt in order to decrypt ZigBee packets. Without it the best * we can do is parse the security header and give up. */ @@ -45,25 +47,36 @@ #include <gcrypt.h> #endif /* HAVE_LIBGCRYPT */ +#include "packet-ieee802154.h" #include "packet-zbee.h" +#include "packet-zbee-nwk.h" #include "packet-zbee-security.h" /* Helper Functions */ #ifdef HAVE_LIBGCRYPT -static gboolean zbee_sec_ccm_decrypt(const gchar *, const gchar *, const gchar *, const gchar *, gchar *, guint, guint, guint); -static guint8 * zbee_sec_key_hash(guint8 *, guint8, packet_info *); -static void zbee_sec_make_nonce (guint8 *, zbee_security_packet *); +static gboolean zbee_sec_ccm_decrypt(const gchar *, const gchar *, const gchar *, const gchar *, gchar *, + guint, guint, guint); +static guint8 * zbee_sec_key_hash(guint8 *, guint8, guint8 *); +static void zbee_sec_make_nonce (zbee_security_packet *, guint8 *); +static gboolean zbee_sec_decrypt_payload(zbee_security_packet *, const gchar *, const gchar, guint8 *, + guint, guint, guint8 *); #endif -static void zbee_security_parse_prefs(void); +static gboolean zbee_security_parse_key(const gchar *, guint8 *, gboolean); +static void proto_init_zbee_security(void); /* Field pointers. */ +#if 0 static int hf_zbee_sec_level = -1; -static int hf_zbee_sec_key = -1; +#endif +static int hf_zbee_sec_key_id = -1; static int hf_zbee_sec_nonce = -1; static int hf_zbee_sec_counter = -1; -static int hf_zbee_sec_src = -1; +static int hf_zbee_sec_src64 = -1; +static int hf_zbee_sec_isrc64 = -1; static int hf_zbee_sec_key_seqno = -1; static int hf_zbee_sec_mic = -1; +static int hf_zbee_sec_key_origin = -1; +static int hf_zbee_sec_src64_origin = -1; /* Subtree pointers. */ static gint ett_zbee_sec = -1; @@ -79,6 +92,7 @@ static const value_string zbee_sec_key_names[] = { { 0, NULL } }; +#if 0 /* These aren't really used anymore, as ZigBee no longer includes them in the * security control field. If we were to display them all we would ever see is * security level 0. @@ -94,6 +108,7 @@ static const value_string zbee_sec_level_names[] = { { ZBEE_SEC_ENC_MIC128, "Encryption, 128-bit MIC" }, { 0, NULL } }; +#endif /* The ZigBee security level, in enum_val_t for the security preferences. */ static enum_val_t zbee_sec_level_enums[] = { @@ -108,22 +123,78 @@ static enum_val_t zbee_sec_level_enums[] = { { NULL, NULL, 0 } }; -/* Network Key. */ -static gboolean zbee_sec_have_nwk_key = FALSE; -static guint8 zbee_sec_nwk_key[ZBEE_SEC_CONST_KEYSIZE]; +static gint gPREF_zbee_sec_level = ZBEE_SEC_ENC_MIC32; +static uat_t *zbee_sec_key_table_uat; + +static const value_string byte_order_vals[] = { + { 0, "Normal"}, + { 1, "Reverse"}, + { 0, NULL } +}; + +/* UAT Key Entry */ +typedef struct _uat_key_record_t { + gchar *string; + gint byte_order; + gchar *label; + guint8 key[ZBEE_SEC_CONST_KEYSIZE]; +} uat_key_record_t; + +/* */ +static uat_key_record_t *uat_key_records = NULL; +static guint num_uat_key_records = 0; + +static void* uat_key_record_copy_cb(void* n, const void* o, unsigned siz _U_) { + uat_key_record_t* new_key = n; + const uat_key_record_t* old_key = o; + + if (old_key->string) { + new_key->string = g_strdup(old_key->string); + } else { + new_key->string = NULL; + } + + if (old_key->label) { + new_key->label = g_strdup(old_key->label); + } else { + new_key->label = NULL; + } + + return new_key; +} + +static void uat_key_record_update_cb(void* r, const char** err) { + uat_key_record_t* rec = r; + + if (rec->string == NULL) { + *err = ep_strdup_printf("Key can't be blank"); + } else { + g_strstrip(rec->string); + + if (rec->string[0] != 0) { + *err = NULL; + if ( !zbee_security_parse_key(rec->string, rec->key, rec->byte_order) ) { + *err = ep_strdup_printf("Expecting %d hexadecimal bytes or\n" + "a %d character double-quoted string", ZBEE_SEC_CONST_KEYSIZE, ZBEE_SEC_CONST_KEYSIZE); + } + } else { + *err = ep_strdup_printf("Key can't be blank"); + } + } +} -/* Trust-Center Link Key. */ -static gboolean zbee_sec_have_tclink_key = FALSE; -static guint8 zbee_sec_tclink_key[ZBEE_SEC_CONST_KEYSIZE]; +static void uat_key_record_free_cb(void*r) { + uat_key_record_t* key = r; -/* Trust-Center Extended Address */ -static guint64 zbee_sec_tcaddr = 0; + if (key->string) g_free(key->string); + if (key->label) g_free(key->label); +} -/* ZigBee Security Preferences. */ -static gint gPREF_zbee_sec_level = ZBEE_SEC_ENC_MIC32; -static const gchar * gPREF_zbee_sec_nwk_key = NULL; -static const gchar * gPREF_zbee_sec_tcaddr = NULL; -static const gchar * gPREF_zbee_sec_tclink_key = NULL; +UAT_CSTRING_CB_DEF(uat_key_records, string, uat_key_record_t) +UAT_VS_DEF(uat_key_records, byte_order, uat_key_record_t, 0, "Normal") +UAT_CSTRING_CB_DEF(uat_key_records, label, uat_key_record_t) + +static GSList *zbee_pc_keyring = NULL; /* * Enable this macro to use libgcrypt's CBC_MAC mode for the authentication @@ -137,8 +208,8 @@ static const gchar * gPREF_zbee_sec_tclink_key = NULL; * NAME * zbee_security_register * DESCRIPTION - * Called to initialize the security dissectors. Roughly the - * equivalent of proto_register_* + * Called by proto_register_zbee_nwk() to initialize the security + * dissectors. * PARAMETERS * module_t zbee_prefs - Prefs module to load preferences under. * RETURNS @@ -148,12 +219,13 @@ static const gchar * gPREF_zbee_sec_tclink_key = NULL; void zbee_security_register(module_t *zbee_prefs, int proto) { static hf_register_info hf[] = { +#if 0 { &hf_zbee_sec_level, { "Level", "zbee.sec.level", FT_UINT8, BASE_HEX, VALS(zbee_sec_level_names), ZBEE_SEC_CONTROL_LEVEL, NULL, HFILL }}, - - { &hf_zbee_sec_key, - { "Key", "zbee.sec.key", FT_UINT8, BASE_HEX, VALS(zbee_sec_key_names), ZBEE_SEC_CONTROL_KEY, +#endif + { &hf_zbee_sec_key_id, + { "Key Id", "zbee.sec.key", FT_UINT8, BASE_HEX, VALS(zbee_sec_key_names), ZBEE_SEC_CONTROL_KEY, NULL, HFILL }}, { &hf_zbee_sec_nonce, @@ -164,8 +236,8 @@ void zbee_security_register(module_t *zbee_prefs, int proto) { "Frame Counter", "zbee.sec.counter", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, - { &hf_zbee_sec_src, - { "Source", "zbee.sec.src", FT_UINT64, BASE_HEX, NULL, 0x0, + { &hf_zbee_sec_src64, + { "Source", "zbee.sec.src64", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_zbee_sec_key_seqno, @@ -174,6 +246,11 @@ void zbee_security_register(module_t *zbee_prefs, int proto) { &hf_zbee_sec_mic, { "Message Integrity Code", "zbee.sec.mic", FT_BYTES, BASE_NONE, NULL, 0x0, + + NULL, HFILL }}, + + { &hf_zbee_sec_key_origin, + { "Key Origin", "zbee.sec.key.origin", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL }} }; @@ -182,135 +259,136 @@ void zbee_security_register(module_t *zbee_prefs, int proto) &ett_zbee_sec_control }; + static uat_field_t key_uat_fields[] = { + UAT_FLD_CSTRING(uat_key_records, string, "Key", + "A 16-byte key in hexadecimal with optional dash-,\n" + "colon-, or space-separator characters, or a\n" + "a 16-character string in double-quotes."), + UAT_FLD_VS(uat_key_records, byte_order, "Byte Order", byte_order_vals, + "Byte order of key."), + UAT_FLD_LSTRING(uat_key_records, label, "Label", "User label for key."), + UAT_END_FIELDS + }; + /* If no prefs module was supplied, register our own. */ if (zbee_prefs == NULL) { - zbee_prefs = prefs_register_protocol(proto, zbee_security_parse_prefs); + zbee_prefs = prefs_register_protocol(proto, NULL); } /* Register preferences */ prefs_register_enum_preference(zbee_prefs, "seclevel", "Security Level", - "Specifies the security level to use in the decryption process. This value is ignored for ZigBee 2004 and unsecured networks.", + "Specifies the security level to use in the\n" + "decryption process. This value is ignored\n" + "for ZigBee 2004 and unsecured networks.", &gPREF_zbee_sec_level, zbee_sec_level_enums, FALSE); - prefs_register_string_preference(zbee_prefs, "nwkkey", "Network Key", - "Specifies the network key to use for decryption.", - &gPREF_zbee_sec_nwk_key); - prefs_register_string_preference(zbee_prefs, "tcaddr", "Trust Center Address", - "The Extended address of the trust center.", - &gPREF_zbee_sec_tcaddr); - prefs_register_string_preference(zbee_prefs, "tclinkkey", "Trust Center Link Key", - "Specifies the trust center link key to use for decryption.", - &gPREF_zbee_sec_tclink_key); + + zbee_sec_key_table_uat = uat_new("Pre-configured Keys", + sizeof(uat_key_record_t), + "zigbee_pc_keys", + TRUE, + (void*) &uat_key_records, + &num_uat_key_records, + UAT_CAT_FFMT, + NULL, /* TODO: ptr to help manual? */ + uat_key_record_copy_cb, + uat_key_record_update_cb, + uat_key_record_free_cb, + NULL, /* TODO: post_update */ + key_uat_fields ); + + prefs_register_uat_preference(zbee_prefs, + "key_table", + "Pre-configured Keys", + "Pre-configured link or network keys.", + zbee_sec_key_table_uat); proto_register_field_array(proto, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); + + /* Register the init routine. */ + register_init_routine(proto_init_zbee_security); } /* zbee_security_register */ /*FUNCTION:------------------------------------------------------ * NAME * zbee_security_parse_key * DESCRIPTION - * Parses a key string into a buffer. + * Parses a key string from left to right into a buffer with + * increasing (normal byte order) or decreasing (reverse byte + * order) address. * PARAMETERS - * const gchar * key_str; - * guint8 key_buf; + * const gchar *key_str - pointer to the string + * guint8 *key_buf - destination buffer in memory + * gboolean big_end - fill key_buf with incrementing address * RETURNS * gboolean *--------------------------------------------------------------- */ static gboolean -zbee_security_parse_key(const gchar *key_str, guint8 *key_buf) +zbee_security_parse_key(const gchar *key_str, guint8 *key_buf, gboolean byte_order) { - int i; + int i, j; gchar temp; + gboolean string_mode = FALSE; /* Clear the key. */ memset(key_buf, 0, ZBEE_SEC_CONST_KEYSIZE); if (key_str == NULL) { return FALSE; } + /* - * Attempt to parse the key string. The key string must represent - * exactly 16 bytes in hexadecimal format with the following - * separators: ':', '-', " ", or no separator at all. Start by - * getting the first character. + * Attempt to parse the key string. The key string must + * be at least 16 pairs of hexidecimal digits with the + * following optional separators: ':', '-', " ", or 16 + * alphanumeric characters after a double-quote. */ - temp = *(key_str++); + if ( (temp = *key_str++) == '"') { + string_mode = TRUE; + temp = *key_str++; + } + + j = byte_order?ZBEE_SEC_CONST_KEYSIZE-1:0; for (i=ZBEE_SEC_CONST_KEYSIZE-1; i>=0; i--) { - /* If this character is a separator, skip it. */ - if ((temp == ':') || (temp == '-') || (temp == ' ')) temp = *(key_str++); - /* Process this nibble. */ - if (('0' <= temp) && (temp <= '9')) key_buf[i] |= ((temp-'0')<<4); - else if (('a' <= temp) && (temp <= 'f')) key_buf[i] |= ((temp-'a'+0x0a)<<4); - else if (('A' <= temp) && (temp <= 'F')) key_buf[i] |= ((temp-'A'+0x0A)<<4); - else return FALSE; - /* Get the next nibble. */ - temp = *(key_str++); - /* Process this nibble. */ - if (('0' <= temp) && (temp <= '9')) key_buf[i] |= (temp-'0'); - else if (('a' <= temp) && (temp <= 'f')) key_buf[i] |= (temp-'a'+0x0a); - else if (('A' <= temp) && (temp <= 'F')) key_buf[i] |= (temp-'A'+0x0A); - else return FALSE; - /* Get the next nibble. */ - temp = *(key_str++); - } /* for */ - /* If we get this far, then the key was good. */ - return TRUE; -} /* zbee_security_parse_key */ + if ( string_mode ) { + if ( g_ascii_isprint(temp) ) { + key_buf[j] = temp; + temp = *key_str++; + } else { + return FALSE; + } + } + else { + /* If this character is a separator, skip it. */ + if ( (temp == ':') || (temp == '-') || (temp == ' ') ) temp = *(key_str++); -/*FUNCTION:------------------------------------------------------ - * NAME - * zbee_security_parse_prefs - * DESCRIPTION - * Parses the security preferences into the parameters needed - * for decryption. - * PARAMETERS - * none - * RETURNS - * void - *--------------------------------------------------------------- - */ -static void -zbee_security_parse_prefs(void) -{ - int i; - const gchar * str_ptr; - gchar temp; + /* Process a nibble. */ + if ( g_ascii_isxdigit (temp) ) key_buf[j] = g_ascii_xdigit_value(temp)<<4; + else return FALSE; + + /* Get the next nibble. */ + temp = *(key_str++); + + /* Process another nibble. */ + if ( g_ascii_isxdigit (temp) ) key_buf[j] |= g_ascii_xdigit_value(temp); + else return FALSE; + + /* Get the next nibble. */ + temp = *(key_str++); + } + + /* Move key_buf pointer */ + if ( byte_order ) { + j--; + } else { + j++; + } - /* Get the network key. */ - zbee_sec_have_nwk_key = zbee_security_parse_key(gPREF_zbee_sec_nwk_key, zbee_sec_nwk_key); - /* Get the trust-center link key. */ - zbee_sec_have_tclink_key = zbee_security_parse_key(gPREF_zbee_sec_tclink_key, zbee_sec_tclink_key); - /* Get the trust-center address. */ - zbee_sec_tcaddr = 0; - str_ptr = gPREF_zbee_sec_tcaddr; - temp = *(str_ptr++); - for (i=0;i<(int)sizeof(guint64);i++) { - /* Except for the first octet, ensure the next character is a - * separator and skip over it. - */ - if ((temp == ':') || (temp == '-')) temp = *(str_ptr++); - else if (i!=0) goto bad_tcaddr; - /* Process this nibble. */ - if (('0' <= temp) && (temp <= '9')) zbee_sec_tcaddr |= ((guint64)(temp-'0'+0x00)<<(8*(sizeof(guint64)-i)-4)); - else if (('a' <= temp) && (temp <= 'f')) zbee_sec_tcaddr |= ((guint64)(temp-'a'+0x0a)<<(8*(sizeof(guint64)-i)-4)); - else if (('A' <= temp) && (temp <= 'F')) zbee_sec_tcaddr |= ((guint64)(temp-'A'+0x0A)<<(8*(sizeof(guint64)-i)-4)); - else goto bad_tcaddr; - /* Get the next nibble. */ - temp = *(str_ptr++); - /* Process this nibble. */ - if (('0' <= temp) && (temp <= '9')) zbee_sec_tcaddr |= ((guint64)(temp-'0'+0x00)<<(8*(sizeof(guint64)-i)-8)); - else if (('a' <= temp) && (temp <= 'f')) zbee_sec_tcaddr |= ((guint64)(temp-'a'+0x0a)<<(8*(sizeof(guint64)-i)-8)); - else if (('A' <= temp) && (temp <= 'F')) zbee_sec_tcaddr |= ((guint64)(temp-'A'+0x0A)<<(8*(sizeof(guint64)-i)-8)); - else goto bad_tcaddr; - /* Get the next nibble. */ - temp = *(str_ptr++); } /* for */ - /* Done */ - return; -bad_tcaddr: - zbee_sec_tcaddr = 0; -} /* zbee_security_parse_prefs */ + /* If we get this far, then the key was good. */ + return TRUE; +} /* zbee_security_parse_key */ /*FUNCTION:------------------------------------------------------ * NAME @@ -328,8 +406,6 @@ zbee_security_handoff(void) { /* Lookup the data dissector. */ data_handle = find_dissector("data"); - /* Parse the security prefs. */ - zbee_security_parse_prefs(); } /* zbee_security_handoff */ /*FUNCTION:------------------------------------------------------ @@ -343,35 +419,49 @@ zbee_security_handoff(void) * handle internally and return NULL. * PARAMETERS * tvbuff_t *tvb - pointer to buffer containing raw packet. - * packet_into *pinfo - pointer to packet information fields + * packet_info *pinfo - pointer to packet information fields * proto_tree *tree - pointer to data tree Wireshark uses to display packet. * guint offset - pointer to the start of the auxilliary security header. - * guint64 src - extended source address, or 0 if unknown. + * guint64 src64 - extended source address, or 0 if unknown. * RETURNS * tvbuff_t * *--------------------------------------------------------------- */ tvbuff_t * -dissect_zbee_secure(tvbuff_t *tvb, packet_info *pinfo, proto_tree* tree, guint offset, guint64 src) +dissect_zbee_secure(tvbuff_t *tvb, packet_info *pinfo, proto_tree* tree, guint offset, guint64 src64) { - proto_tree * sec_tree = NULL; - proto_item * sec_root; - proto_tree * field_tree; - proto_item * ti; + proto_tree *sec_tree = NULL; + proto_item *sec_root; + proto_tree *field_tree; + proto_item *ti; zbee_security_packet packet; guint mic_len; guint payload_len; - tvbuff_t * payload_tvb; + tvbuff_t *payload_tvb; #ifdef HAVE_LIBGCRYPT - const guint8 * enc_buffer; - guint8 * dec_buffer; - guint8 * key_buffer; - guint8 nonce[ZBEE_SEC_CONST_NONCE_LEN]; + const guint8 *enc_buffer; + guint8 *dec_buffer; + guint8 buffer[ZBEE_SEC_CONST_BLOCKSIZE+1]; + guint8 *key_buffer = buffer; + gboolean decrypted; + GSList **nwk_keyring; + GSList *GSList_i; + key_record_t *key_rec = NULL; #endif - - /* Create a substree for the security information. */ + zbee_nwk_hints_t *nwk_hints; + ieee802154_hints_t *ieee_hints; + ieee802154_map_rec *map_rec = NULL; + + /* Init */ + memset(&packet, 0, sizeof(zbee_security_packet)); + + /* Get pointers to any useful frame data from lower layers */ + nwk_hints = p_get_proto_data(pinfo->fd, proto_get_id_by_filter_name(ZBEE_PROTOABBREV_NWK)); + ieee_hints = p_get_proto_data(pinfo->fd, proto_get_id_by_filter_name(IEEE802154_PROTOABBREV_WPAN)); + + /* Create a subtree for the security information. */ if (tree) { sec_root = proto_tree_add_text(tree, tvb, offset, tvb_length_remaining(tvb, offset), "ZigBee Security Header"); sec_tree = proto_item_add_subtree(sec_root, ett_zbee_sec); @@ -379,14 +469,17 @@ dissect_zbee_secure(tvbuff_t *tvb, packet_info *pinfo, proto_tree* tree, guint o /* Get and display the Security control field */ packet.control = tvb_get_guint8(tvb, offset); + /* Patch the security level. */ packet.control &= ~ZBEE_SEC_CONTROL_LEVEL; packet.control |= (ZBEE_SEC_CONTROL_LEVEL & gPREF_zbee_sec_level); + /* * Eww, I think I just threw up a little... ZigBee requires this field * to be patched before computing the MIC, but we don't have write-access * to the tvbuff. So we need to allocate a copy of the whole thing just - * so we can fix these 3 bits. + * so we can fix these 3 bits. Memory allocated by ep_tvb_memdup() is + * automatically freed before the next packet is processed. */ #ifdef HAVE_LIBGCRYPT enc_buffer = ep_tvb_memdup(tvb, 0, tvb_length(tvb)); @@ -398,14 +491,16 @@ dissect_zbee_secure(tvbuff_t *tvb, packet_info *pinfo, proto_tree* tree, guint o ((guint8 *)(enc_buffer))[offset] = packet.control; #endif /* HAVE_LIBGCRYPT */ packet.level = zbee_get_bit_field(packet.control, ZBEE_SEC_CONTROL_LEVEL); - packet.key = zbee_get_bit_field(packet.control, ZBEE_SEC_CONTROL_KEY); + packet.key_id = zbee_get_bit_field(packet.control, ZBEE_SEC_CONTROL_KEY); packet.nonce = zbee_get_bit_field(packet.control, ZBEE_SEC_CONTROL_NONCE); if (tree) { ti = proto_tree_add_text(sec_tree, tvb, offset, sizeof(guint8), "Security Control Field"); field_tree = proto_item_add_subtree(ti, ett_zbee_sec_control); - proto_tree_add_uint(field_tree, hf_zbee_sec_key, tvb, offset, sizeof(guint8), packet.control & ZBEE_SEC_CONTROL_KEY); - proto_tree_add_boolean(field_tree, hf_zbee_sec_nonce, tvb, offset, sizeof(guint8), packet.control & ZBEE_SEC_CONTROL_NONCE); + proto_tree_add_uint(field_tree, hf_zbee_sec_key_id, tvb, offset, sizeof(guint8), + packet.control & ZBEE_SEC_CONTROL_KEY); + proto_tree_add_boolean(field_tree, hf_zbee_sec_nonce, tvb, offset, sizeof(guint8), + packet.control & ZBEE_SEC_CONTROL_NONCE); } offset += sizeof(guint8); @@ -417,21 +512,32 @@ dissect_zbee_secure(tvbuff_t *tvb, packet_info *pinfo, proto_tree* tree, guint o offset += sizeof(guint32); if (packet.nonce) { - /* Get and display the source address. */ - packet.src = tvb_get_letoh64(tvb, offset); + /* Get and display the source address of the device that secured this payload. */ + packet.src64 = tvb_get_letoh64(tvb, offset); if (tree) { - proto_tree_add_eui64(sec_tree, hf_zbee_sec_src, tvb, offset, sizeof(guint64), packet.src); + proto_tree_add_eui64(sec_tree, hf_zbee_sec_src64, tvb, offset, sizeof(guint64), packet.src64); } offset += sizeof(guint64); + } else { - /* This field is required in the security decryption process, so - * fill it in in case the higher layer provided it. - */ - packet.src = src; + /* Look for a source address in hints */ + switch ( packet.key_id ) { + case ZBEE_SEC_KEY_NWK: + /* use the ieee extended source address for NWK decryption */ + if ( ieee_hints && (map_rec = ieee_hints->map_rec) ) packet.src64 = map_rec->addr64; + else if (tree) proto_tree_add_text(sec_tree, tvb, 0, 0, "Source: Unknown"); + break; + + default: + /* use the nwk extended source address for APS decryption */ + if ( nwk_hints && (map_rec = nwk_hints->map_rec) ) packet.src64 = map_rec->addr64; + else if (tree) proto_tree_add_text(sec_tree, tvb, 0, 0, "Source: Unknown"); + break; + } } - if (packet.key == ZBEE_SEC_KEY_NWK) { + if (packet.key_id == ZBEE_SEC_KEY_NWK) { /* Get and display the key sequence number. */ packet.key_seqno = tvb_get_guint8(tvb, offset); if (tree) { @@ -441,7 +547,7 @@ dissect_zbee_secure(tvbuff_t *tvb, packet_info *pinfo, proto_tree* tree, guint o } /* Determine the length of the MIC. */ - switch (packet.level){ + switch (packet.level) { case ZBEE_SEC_ENC: case ZBEE_SEC_NONE: default: @@ -471,7 +577,8 @@ dissect_zbee_secure(tvbuff_t *tvb, packet_info *pinfo, proto_tree* tree, guint o if (mic_len) { /* Display the MIC. */ if (tree) { - ti = proto_tree_add_bytes(sec_tree, hf_zbee_sec_mic, tvb, tvb_length(tvb)-mic_len, mic_len, ep_tvb_memdup(tvb, tvb_length(tvb)-mic_len, mic_len)); + ti = proto_tree_add_bytes(sec_tree, hf_zbee_sec_mic, tvb, tvb_length(tvb)-mic_len, + mic_len, ep_tvb_memdup(tvb, tvb_length(tvb)-mic_len, mic_len)); } } @@ -483,105 +590,118 @@ dissect_zbee_secure(tvbuff_t *tvb, packet_info *pinfo, proto_tree* tree, guint o (packet.level == ZBEE_SEC_MIC32) || (packet.level == ZBEE_SEC_MIC64) || (packet.level == ZBEE_SEC_MIC128)) { + /* Payload is only integrity protected. Just return the sub-tvbuff. */ return tvb_new_subset(tvb, offset, payload_len, payload_len); } #ifdef HAVE_LIBGCRYPT - /* Ensure we have enough security material to decrypt this payload. */ - switch (packet.key) { - /* Network Keys use the shared network key. */ - case ZBEE_SEC_KEY_NWK: - if (!zbee_sec_have_nwk_key) { - /* Without a key we can't decrypt (if we could what good would security be?)*/ - goto decrypt_failed; - } - if (packet.src == 0) { - /* Without the extended source address, we can't create the nonce. */ - goto decrypt_failed; - } - /* The key, is the network key. */ - key_buffer = zbee_sec_nwk_key; - break; - - /* Link Key might use the trust center link key. */ - case ZBEE_SEC_KEY_LINK: - if (!zbee_sec_have_tclink_key) { - /* Without a key we can't decrypt. */ - goto decrypt_failed; - } - if ((packet.src == 0) && (zbee_sec_tcaddr == 0)){ - /* Without the extended source address, we can't create the nonce. */ - goto decrypt_failed; - } - else if (packet.src == 0) { - packet.src = zbee_sec_tcaddr; - } - key_buffer = zbee_sec_tclink_key; - break; + /* Allocate memory to decrypt the payload into. */ + dec_buffer = g_malloc(payload_len); - /* Key-Transport Key should use the trust center link key. */ - case ZBEE_SEC_KEY_TRANSPORT: - if (!zbee_sec_have_tclink_key) { - /* Without a key we can't decrypt. */ - goto decrypt_failed; - } - if ((packet.src == 0) && (zbee_sec_tcaddr == 0)){ - /* Without the extended source address, we can't create the nonce. */ - goto decrypt_failed; - } - else if (packet.src == 0) { - packet.src = zbee_sec_tcaddr; + decrypted = FALSE; + if ( packet.src64 ) { + if (pinfo->fd->flags.visited) { + if ( nwk_hints ) { + /* Use previously found key */ + switch ( packet.key_id ) { + case ZBEE_SEC_KEY_NWK: + if ( key_rec = nwk_hints->nwk ) { + decrypted = zbee_sec_decrypt_payload( &packet, enc_buffer, offset, dec_buffer, + payload_len, mic_len, nwk_hints->nwk->key); + } + break; + + default: + if ( key_rec = nwk_hints->link ) { + decrypted = zbee_sec_decrypt_payload( &packet, enc_buffer, offset, dec_buffer, + payload_len, mic_len, nwk_hints->link->key); + } + break; + } } - key_buffer = zbee_sec_key_hash(zbee_sec_tclink_key, 0x00, pinfo); - break; - - /* Key-Load Key should use the trust center link key. */ - case ZBEE_SEC_KEY_LOAD: - if (!zbee_sec_have_tclink_key) { - /* Without a key we can't decrypt. */ - goto decrypt_failed; + } /* ( !pinfo->fd->flags.visited ) */ + else { + /* We only search for sniffed keys in the first pass, + * to save time, and because decrypting with keys + * transported in future packets is cheating */ + + /* Lookup NWK and link key in hash for this pan. */ + /* This overkill approach is a placeholder for a hash that looks up + * a key ring for a link key associated with a pair of devices. + */ + if ( nwk_hints ) { + nwk_keyring = g_hash_table_lookup(zbee_table_nwk_keyring, &nwk_hints->src_pan); + + if ( nwk_keyring ) { + GSList_i = *nwk_keyring; + while ( GSList_i && !decrypted ) { + decrypted = zbee_sec_decrypt_payload( &packet, enc_buffer, offset, dec_buffer, + payload_len, mic_len, ((key_record_t *)(GSList_i->data))->key); + + if (decrypted) { + /* save pointer to the successful key record */ + switch (packet.key_id) { + case ZBEE_SEC_KEY_NWK: + key_rec = nwk_hints->nwk = GSList_i->data; + break; + + default: + key_rec = nwk_hints->link = GSList_i->data; + break; + } + } else { + GSList_i = g_slist_next(GSList_i); + } + } + } } - if ((packet.src == 0) && (zbee_sec_tcaddr == 0)){ - /* Without the extended source address, we can't create the nonce. */ - goto decrypt_failed; + + /* Loop through user's password table for preconfigured keys, our last resort */ + GSList_i = zbee_pc_keyring; + while ( GSList_i && !decrypted ) { + decrypted = zbee_sec_decrypt_payload( &packet, enc_buffer, offset, dec_buffer, + payload_len, mic_len, ((key_record_t *)(GSList_i->data))->key); + + if (decrypted) { + /* save pointer to the successful key record */ + switch (packet.key_id) { + case ZBEE_SEC_KEY_NWK: + key_rec = nwk_hints->nwk = GSList_i->data; + break; + + default: + key_rec = nwk_hints->link = GSList_i->data; + break; + } + } else { + GSList_i = g_slist_next(GSList_i); + } } - else if (packet.src == 0) { - packet.src = zbee_sec_tcaddr; + } /* ( ! pinfo->fd->flags.visited ) */ + } /* ( packet.src64 ) */ + + if ( decrypted ) { + if ( tree && key_rec ) { + if ( key_rec->frame_num == ZBEE_SEC_PC_KEY ) { + ti = proto_tree_add_text(sec_tree, tvb, 0, 0, "Decryption Key: %s", key_rec->label); + } else { + ti = proto_tree_add_uint(sec_tree, hf_zbee_sec_key_origin, tvb, 0, 0, + key_rec->frame_num); } - key_buffer = zbee_sec_key_hash(zbee_sec_tclink_key, 0x02, pinfo); - break; + PROTO_ITEM_SET_GENERATED(ti); + } - default: - goto decrypt_failed; - } /* switch */ + /* Found a key that worked, setup the new tvbuff_t and return */ + payload_tvb = tvb_new_child_real_data(tvb, dec_buffer, payload_len, payload_len); + tvb_set_free_cb(payload_tvb, g_free); /* set up callback to free dec_buffer */ + add_new_data_source(pinfo, payload_tvb, "Decrypted ZigBee Payload"); - /* Create the nonce. */ - zbee_sec_make_nonce(nonce, &packet); - /* Allocate memory to decrypt the payload into. */ - dec_buffer = g_malloc(payload_len); - /* Perform Decryption. */ - if (!zbee_sec_ccm_decrypt(key_buffer, /* key */ - nonce, /* Nonce */ - enc_buffer, /* a, length l(a) */ - enc_buffer+offset, /* c, length l(c) = l(m) + M */ - dec_buffer, /* m, length l(m) */ - offset, /* l(a) */ - payload_len, /* l(m) */ - mic_len)) { /* M */ - /* Decryption Failed! */ - g_free(dec_buffer); - goto decrypt_failed; + /* Done! */ + return payload_tvb; } - /* Setup the new tvbuff_t and return */ - payload_tvb = tvb_new_child_real_data(tvb, dec_buffer, payload_len, payload_len); - tvb_set_free_cb(payload_tvb, g_free); - add_new_data_source(pinfo, payload_tvb, "Decrypted ZigBee Payload"); - /* Done! */ - return payload_tvb; - -decrypt_failed: + g_free(dec_buffer); #endif /* HAVE_LIBGCRYPT */ /* Add expert info. */ @@ -592,35 +712,97 @@ decrypt_failed: call_dissector(data_handle, payload_tvb, pinfo, tree); /* Couldn't decrypt, so return NULL. */ return NULL; - } /* dissect_zbee_secure */ #ifdef HAVE_LIBGCRYPT /*FUNCTION:------------------------------------------------------ * NAME + * zbee_sec_decrypt_payload + * DESCRIPTION + * Creates a nonce and decrypts a secured payload. + * PARAMETERS + * gchar *nonce - Nonce Buffer. + * zbee_security_packet *packet - Security information. + * RETURNS + * void + *--------------------------------------------------------------- + */ +static gboolean +zbee_sec_decrypt_payload(zbee_security_packet *packet, const gchar *enc_buffer, const gchar offset, guint8 *dec_buffer, + guint payload_len, guint mic_len, guint8 *key) +{ + guint8 nonce[ZBEE_SEC_CONST_NONCE_LEN]; + guint8 buffer[ZBEE_SEC_CONST_BLOCKSIZE+1]; + guint8 *key_buffer = buffer; + + switch (packet->key_id) { + case ZBEE_SEC_KEY_NWK: + /* Decrypt with the PAN's current network key */ + case ZBEE_SEC_KEY_LINK: + /* Decrypt with the unhashed link key assigned by the trust center to this + * source/destination pair */ + key_buffer = key; + break; + + case ZBEE_SEC_KEY_TRANSPORT: + /* Decrypt with a Key-Transport key, a hashed link key that protects network + * keys sent from the trust center */ + zbee_sec_key_hash(key, 0x00, buffer); + key_buffer = buffer; + break; + + case ZBEE_SEC_KEY_LOAD: + /* Decrypt with a Key-Load key, a hashed link key that protects link keys + * sent from the trust center. */ + zbee_sec_key_hash(key, 0x02, buffer); + key_buffer = buffer; + break; + + default: + break; + } /* switch */ + + /* Perform Decryption. */ + zbee_sec_make_nonce(packet, nonce); + + if ( zbee_sec_ccm_decrypt(key_buffer, /* key */ + nonce, /* Nonce */ + enc_buffer, /* a, length l(a) */ + enc_buffer+offset, /* c, length l(c) = l(m) + M */ + dec_buffer, /* m, length l(m) */ + offset, /* l(a) */ + payload_len, /* l(m) */ + mic_len) ) { /* M */ + return TRUE; + } + else return FALSE; +} + +/*FUNCTION:------------------------------------------------------ + * NAME * zbee_sec_make_nonce * DESCRIPTION * Fills in the ZigBee security nonce from the provided security * packet structure. * PARAMETERS - * gchar *nonce - Nonce Buffer. * zbee_security_packet *packet - Security information. + * gchar *nonce - Nonce Buffer. * RETURNS * void *--------------------------------------------------------------- */ static void -zbee_sec_make_nonce(guint8 *nonce, zbee_security_packet *packet) +zbee_sec_make_nonce(zbee_security_packet *packet, guint8 *nonce) { /* First 8 bytes are the extended source address (little endian). */ - *(nonce++) = (guint8)((packet->src)>>0 & 0xff); - *(nonce++) = (guint8)((packet->src)>>8 & 0xff); - *(nonce++) = (guint8)((packet->src)>>16 & 0xff); - *(nonce++) = (guint8)((packet->src)>>24 & 0xff); - *(nonce++) = (guint8)((packet->src)>>32 & 0xff); - *(nonce++) = (guint8)((packet->src)>>40 & 0xff); - *(nonce++) = (guint8)((packet->src)>>48 & 0xff); - *(nonce++) = (guint8)((packet->src)>>56 & 0xff); + *(nonce++) = (guint8)((packet->src64)>>0 & 0xff); + *(nonce++) = (guint8)((packet->src64)>>8 & 0xff); + *(nonce++) = (guint8)((packet->src64)>>16 & 0xff); + *(nonce++) = (guint8)((packet->src64)>>24 & 0xff); + *(nonce++) = (guint8)((packet->src64)>>32 & 0xff); + *(nonce++) = (guint8)((packet->src64)>>40 & 0xff); + *(nonce++) = (guint8)((packet->src64)>>48 & 0xff); + *(nonce++) = (guint8)((packet->src64)>>56 & 0xff); /* Next 4 bytes are the frame counter (little endian). */ *(nonce++) = (guint8)((packet->counter)>>0 & 0xff); *(nonce++) = (guint8)((packet->counter)>>8 & 0xff); @@ -806,7 +988,8 @@ zbee_sec_ccm_decrypt(const gchar *key, /* Input */ for (i=0;i<l_a;i++,j++) { if (j>=ZBEE_SEC_CONST_BLOCKSIZE) { /* Generate the next cipher block. */ - if (gcry_cipher_encrypt(cipher_hd, cipher_out, ZBEE_SEC_CONST_BLOCKSIZE, cipher_in, ZBEE_SEC_CONST_BLOCKSIZE)) { + if (gcry_cipher_encrypt(cipher_hd, cipher_out, ZBEE_SEC_CONST_BLOCKSIZE, cipher_in, + ZBEE_SEC_CONST_BLOCKSIZE)) { gcry_cipher_close(cipher_hd); return FALSE; } @@ -824,7 +1007,8 @@ zbee_sec_ccm_decrypt(const gchar *key, /* Input */ for (i=0; i<l_m; i++, j++) { if (j>=ZBEE_SEC_CONST_BLOCKSIZE) { /* Generate the next cipher block. */ - if (gcry_cipher_encrypt(cipher_hd, cipher_out, ZBEE_SEC_CONST_BLOCKSIZE, cipher_in, ZBEE_SEC_CONST_BLOCKSIZE)) { + if (gcry_cipher_encrypt(cipher_hd, cipher_out, ZBEE_SEC_CONST_BLOCKSIZE, cipher_in, + ZBEE_SEC_CONST_BLOCKSIZE)) { gcry_cipher_close(cipher_hd); return FALSE; } @@ -976,15 +1160,15 @@ zbee_sec_hash(guint8 *input, guint input_len, guint8 *output) * PARAMETERS * guint8 *key - ZigBee Security Key (must be ZBEE_SEC_CONST_KEYSIZE) in length. * guint8 input - ZigBee CCM* Nonce (must be ZBEE_SEC_CONST_NONCE_LEN) in length. + * packet_info *pinfo - pointer to packet information fields * RETURNS * guint8* *--------------------------------------------------------------- */ static guint8 * -zbee_sec_key_hash(guint8 *key, guint8 input, packet_info *pinfo _U_) +zbee_sec_key_hash(guint8 *key, guint8 input, guint8 *hash_out) { guint8 hash_in[2*ZBEE_SEC_CONST_BLOCKSIZE]; - guint8 * hash_out = ep_alloc(ZBEE_SEC_CONST_BLOCKSIZE+1); int i; static const guint8 ipad = 0x36; static const guint8 opad = 0x5c; @@ -1004,3 +1188,36 @@ zbee_sec_key_hash(guint8 *key, guint8 input, packet_info *pinfo _U_) return hash_out; } /* zbee_sec_key_hash */ #endif /* HAVE_LIBGCRYPT */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * proto_init_zbee_security + * DESCRIPTION + * Init routine for the + * PARAMETERS + * none + * RETURNS + * void + *--------------------------------------------------------------- + */ +static void +proto_init_zbee_security(void) +{ + guint i; + key_record_t key_record; + + /* empty the key ring */ + if (zbee_pc_keyring) { + g_slist_free(zbee_pc_keyring); + zbee_pc_keyring = NULL; + } + + /* Load the pre-configured slist from the UAT. */ + for (i=0; (uat_key_records) && (i<num_uat_key_records) ; i++) { + key_record.frame_num = ZBEE_SEC_PC_KEY; /* means it's a user PC key */ + key_record.label = se_strdup(uat_key_records[i].label); + memcpy(&key_record.key, &uat_key_records[i].key, ZBEE_SEC_CONST_KEYSIZE); + + zbee_pc_keyring = g_slist_prepend(zbee_pc_keyring, se_memdup(&key_record, sizeof(key_record_t))); + } /* for */ +} /* proto_init_zbee_security */ |