aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-zbee-security.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-zbee-security.c')
-rw-r--r--epan/dissectors/packet-zbee-security.c715
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 */