wireshark/epan/dissectors/packet-zbee-security.c

1256 lines
49 KiB
C

/* packet-zbee-security.c
* Dissector helper routines for encrypted ZigBee frames.
* By Owen Kirby <osk@exegin.com>; portions by Fred Fierling <fff@exegin.com>
* Copyright 2009 Exegin Technologies Limited
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
/* Include Files */
#include "config.h"
#include <epan/packet.h>
#include <epan/exceptions.h>
#include <epan/prefs.h>
#include <epan/expert.h>
#include <epan/uat.h>
#include <epan/proto_data.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.
*/
#include <wsutil/wsgcrypt.h>
#include <wsutil/pint.h>
#include "packet-ieee802154.h"
#include "packet-zbee.h"
#include "packet-zbee-nwk.h"
#include "packet-zbee-aps.h" /* for ZBEE_APS_CMD_KEY_LENGTH */
#include "packet-zbee-security.h"
/* Helper Functions */
static void 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 *);
static gboolean zbee_security_parse_key(const gchar *, guint8 *, gboolean);
/* Field pointers. */
static int hf_zbee_sec_field = -1;
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_src64 = -1;
static int hf_zbee_sec_key_seqno = -1;
static int hf_zbee_sec_mic = -1;
static int hf_zbee_sec_key = -1;
static int hf_zbee_sec_key_origin = -1;
static int hf_zbee_sec_decryption_key = -1;
/* Subtree pointers. */
static gint ett_zbee_sec = -1;
static gint ett_zbee_sec_control = -1;
static expert_field ei_zbee_sec_encrypted_payload = EI_INIT;
static expert_field ei_zbee_sec_encrypted_payload_sliced = EI_INIT;
static expert_field ei_zbee_sec_extended_source_unknown = EI_INIT;
static const value_string zbee_sec_key_names[] = {
{ ZBEE_SEC_KEY_LINK, "Link Key" },
{ ZBEE_SEC_KEY_NWK, "Network Key" },
{ ZBEE_SEC_KEY_TRANSPORT, "Key-Transport Key" },
{ ZBEE_SEC_KEY_LOAD, "Key-Load Key" },
{ 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.
*/
static const value_string zbee_sec_level_names[] = {
{ ZBEE_SEC_NONE, "None" },
{ ZBEE_SEC_MIC32, "No Encryption, 32-bit MIC" },
{ ZBEE_SEC_MIC64, "No Encryption, 64-bit MIC" },
{ ZBEE_SEC_MIC128, "No Encryption, 128-bit MIC" },
{ ZBEE_SEC_ENC, "Encryption, No MIC" },
{ ZBEE_SEC_ENC_MIC32, "Encryption, 32-bit MIC" },
{ ZBEE_SEC_ENC_MIC64, "Encryption, 64-bit MIC" },
{ ZBEE_SEC_ENC_MIC128, "Encryption, 128-bit MIC" },
{ 0, NULL }
};
#endif
/* The ZigBee security level, in enum_val_t for the security preferences. */
static const enum_val_t zbee_sec_level_enums[] = {
{ "None", "No Security", ZBEE_SEC_NONE },
{ "MIC32", "No Encryption, 32-bit Integrity Protection", ZBEE_SEC_MIC32 },
{ "MIC64", "No Encryption, 64-bit Integrity Protection", ZBEE_SEC_MIC64 },
{ "MIC128", "No Encryption, 128-bit Integrity Protection", ZBEE_SEC_MIC128 },
{ "ENC", "AES-128 Encryption, No Integrity Protection", ZBEE_SEC_ENC },
{ "ENC-MIC32", "AES-128 Encryption, 32-bit Integrity Protection", ZBEE_SEC_ENC_MIC32 },
{ "ENC-MIC64", "AES-128 Encryption, 64-bit Integrity Protection", ZBEE_SEC_ENC_MIC64 },
{ "ENC-MIC128", "AES-128 Encryption, 128-bit Integrity Protection", ZBEE_SEC_ENC_MIC128 },
{ NULL, NULL, 0 }
};
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;
guint8 byte_order;
gchar *label;
} uat_key_record_t;
UAT_CSTRING_CB_DEF(uat_key_records, string, uat_key_record_t)
UAT_VS_DEF(uat_key_records, byte_order, uat_key_record_t, guint8, 0, "Normal")
UAT_CSTRING_CB_DEF(uat_key_records, label, uat_key_record_t)
static GSList *zbee_pc_keyring = NULL;
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, size_t siz _U_) {
uat_key_record_t* new_key = (uat_key_record_t *)n;
const uat_key_record_t* old_key = (const uat_key_record_t *)o;
new_key->string = g_strdup(old_key->string);
new_key->label = g_strdup(old_key->label);
new_key->byte_order = old_key->byte_order;
return new_key;
}
static gboolean uat_key_record_update_cb(void* r, char** err) {
uat_key_record_t* rec = (uat_key_record_t *)r;
guint8 key[ZBEE_SEC_CONST_KEYSIZE];
if (rec->string == NULL) {
*err = g_strdup("Key can't be blank");
return FALSE;
} else {
g_strstrip(rec->string);
if (rec->string[0] != 0) {
*err = NULL;
if ( !zbee_security_parse_key(rec->string, key, rec->byte_order) ) {
*err = ws_strdup_printf("Expecting %d hexadecimal bytes or\n"
"a %d character double-quoted string", ZBEE_SEC_CONST_KEYSIZE, ZBEE_SEC_CONST_KEYSIZE);
return FALSE;
}
} else {
*err = g_strdup("Key can't be blank");
return FALSE;
}
}
return TRUE;
}
static void uat_key_record_free_cb(void*r) {
uat_key_record_t* key = (uat_key_record_t *)r;
g_free(key->string);
g_free(key->label);
}
static void zbee_free_key_record(gpointer ptr)
{
key_record_t *k = (key_record_t *)ptr;
g_free(k->label);
g_free(k);
}
static void uat_key_record_post_update(void) {
guint i;
key_record_t key_record;
guint8 key[ZBEE_SEC_CONST_KEYSIZE];
/* empty the key ring */
if (zbee_pc_keyring) {
g_slist_free_full(zbee_pc_keyring, zbee_free_key_record);
zbee_pc_keyring = NULL;
}
/* Load the pre-configured slist from the UAT. */
for (i=0; (uat_key_records) && (i<num_uat_key_records) ; i++) {
if (zbee_security_parse_key(uat_key_records[i].string, key, uat_key_records[i].byte_order)) {
key_record.frame_num = ZBEE_SEC_PC_KEY; /* means it's a user PC key */
key_record.label = g_strdup(uat_key_records[i].label);
memcpy(key_record.key, key, ZBEE_SEC_CONST_KEYSIZE);
zbee_pc_keyring = g_slist_prepend(zbee_pc_keyring, g_memdup2(&key_record, sizeof(key_record_t)));
}
}
}
/*
* Enable this macro to use libgcrypt's CBC_MAC mode for the authentication
* phase. Unfortunately, this is broken, and I don't know why. However, using
* the messier EBC mode (to emulate CCM*) still works fine.
*/
#if 0
#define ZBEE_SEC_USE_GCRYPT_CBC_MAC
#endif
/*FUNCTION:------------------------------------------------------
* NAME
* zbee_security_register
* DESCRIPTION
* Called by proto_register_zbee_nwk() to initialize the security
* dissectors.
* PARAMETERS
* module_t zbee_prefs - Prefs module to load preferences under.
* RETURNS
* none
*---------------------------------------------------------------
*/
void zbee_security_register(module_t *zbee_prefs, int proto)
{
static hf_register_info hf[] = {
{ &hf_zbee_sec_field,
{ "Security Control Field", "zbee.sec.field", FT_UINT8, BASE_HEX, NULL,
0x0, NULL, HFILL }},
{ &hf_zbee_sec_key_id,
{ "Key Id", "zbee.sec.key_id", FT_UINT8, BASE_HEX, VALS(zbee_sec_key_names),
ZBEE_SEC_CONTROL_KEY, NULL, HFILL }},
{ &hf_zbee_sec_nonce,
{ "Extended Nonce", "zbee.sec.ext_nonce", FT_BOOLEAN, 8, NULL, ZBEE_SEC_CONTROL_NONCE,
NULL, HFILL }},
{ &hf_zbee_sec_counter,
{ "Frame Counter", "zbee.sec.counter", FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_zbee_sec_src64,
{ "Extended Source", "zbee.sec.src64", FT_EUI64, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
{ &hf_zbee_sec_key_seqno,
{ "Key Sequence Number", "zbee.sec.key_seqno", FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_zbee_sec_mic,
{ "Message Integrity Code", "zbee.sec.mic", FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
{ &hf_zbee_sec_key,
{ "Key", "zbee.sec.key", 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 }},
{ &hf_zbee_sec_decryption_key,
{ "Key Label", "zbee.sec.decryption_key", FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }}
};
static gint *ett[] = {
&ett_zbee_sec,
&ett_zbee_sec_control
};
static ei_register_info ei[] = {
{ &ei_zbee_sec_encrypted_payload, { "zbee_sec.encrypted_payload", PI_UNDECODED, PI_WARN, "Encrypted Payload", EXPFILL }},
{ &ei_zbee_sec_encrypted_payload_sliced, { "zbee_sec.encrypted_payload_sliced", PI_UNDECODED, PI_WARN, "Encrypted payload, cut short when capturing - can't decrypt", EXPFILL }},
{ &ei_zbee_sec_extended_source_unknown, { "zbee_sec.extended_source_unknown", PI_PROTOCOL, PI_NOTE, "Extended Source: Unknown", EXPFILL }},
};
expert_module_t* expert_zbee_sec;
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_CSTRING(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, NULL);
}
/* Register preferences */
prefs_register_enum_preference(zbee_prefs, "seclevel", "Security Level",
"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);
zbee_sec_key_table_uat = uat_new("Pre-configured Keys",
sizeof(uat_key_record_t),
"zigbee_pc_keys",
TRUE,
&uat_key_records,
&num_uat_key_records,
UAT_AFFECTS_DISSECTION, /* affects dissection of packets, but not set of named fields */
NULL, /* TODO: ptr to help manual? */
uat_key_record_copy_cb,
uat_key_record_update_cb,
uat_key_record_free_cb,
uat_key_record_post_update,
NULL,
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));
expert_zbee_sec = expert_register_protocol(proto);
expert_register_field_array(expert_zbee_sec, ei, array_length(ei));
} /* zbee_security_register */
/*FUNCTION:------------------------------------------------------
* NAME
* zbee_security_parse_key
* DESCRIPTION
* 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 - 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, gboolean byte_order)
{
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
* be at least 16 pairs of hexidecimal digits with the
* following optional separators: ':', '-', " ", or 16
* alphanumeric characters after a double-quote.
*/
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 ( 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++);
/* 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++;
}
} /* for */
/* If we get this far, then the key was good. */
return TRUE;
} /* zbee_security_parse_key */
/*FUNCTION:------------------------------------------------------
* NAME
* dissect_zbee_secure
* DESCRIPTION
* Dissects and decrypts secured ZigBee frames.
*
* Will return a valid tvbuff only if security processing was
* successful. If processing fails, then this function will
* handle internally and return NULL.
* PARAMETERS
* tvbuff_t *tvb - pointer to buffer containing raw packet.
* 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 auxiliary security header.
* 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)
{
proto_tree *sec_tree;
zbee_security_packet packet;
guint mic_len;
gint payload_len;
tvbuff_t *payload_tvb;
proto_item *ti;
proto_item *key_item;
guint8 *enc_buffer;
guint8 *dec_buffer;
gboolean decrypted;
GSList **nwk_keyring;
GSList *GSList_i;
key_record_t *key_rec = NULL;
zbee_nwk_hints_t *nwk_hints;
ieee802154_hints_t *ieee_hints;
ieee802154_map_rec *map_rec = NULL;
static int * const sec_flags[] = {
&hf_zbee_sec_key_id,
&hf_zbee_sec_nonce,
NULL
};
/* Init */
memset(&packet, 0, sizeof(zbee_security_packet));
/* Get pointers to any useful frame data from lower layers */
nwk_hints = (zbee_nwk_hints_t *)p_get_proto_data(wmem_file_scope(), pinfo,
proto_get_id_by_filter_name(ZBEE_PROTOABBREV_NWK), 0);
ieee_hints = (ieee802154_hints_t *)p_get_proto_data(wmem_file_scope(), pinfo,
proto_get_id_by_filter_name(IEEE802154_PROTOABBREV_WPAN), 0);
/* Create a subtree for the security information. */
sec_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_zbee_sec, NULL, "ZigBee Security Header");
/* 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. Memory allocated by tvb_memdup(pinfo->pool,...)
* is automatically freed before the next packet is processed.
*/
enc_buffer = (guint8 *)tvb_memdup(pinfo->pool, tvb, 0, tvb_captured_length(tvb));
/*
* Override the const qualifiers and patch the security level field, we
* know it is safe to overide the const qualifiers because we just
* allocated this memory via tvb_memdup(pinfo->pool,...).
*/
enc_buffer[offset] = packet.control;
packet.level = zbee_get_bit_field(packet.control, ZBEE_SEC_CONTROL_LEVEL);
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);
proto_tree_add_bitmask(sec_tree, tvb, offset, hf_zbee_sec_field, ett_zbee_sec_control, sec_flags, ENC_NA);
offset += 1;
/* Get and display the frame counter field. */
packet.counter = tvb_get_letohl(tvb, offset);
proto_tree_add_uint(sec_tree, hf_zbee_sec_counter, tvb, offset, 4, packet.counter);
offset += 4;
if (packet.nonce) {
/* Get and display the source address of the device that secured this payload. */
packet.src64 = tvb_get_letoh64(tvb, offset);
proto_tree_add_item(sec_tree, hf_zbee_sec_src64, tvb, offset, 8, ENC_LITTLE_ENDIAN);
#if 1
if (!pinfo->fd->visited) {
switch ( packet.key_id ) {
case ZBEE_SEC_KEY_LINK:
if (nwk_hints && ieee_hints) {
/* Map this long address with the nwk layer short address. */
nwk_hints->map_rec = ieee802154_addr_update(&zbee_nwk_map, nwk_hints->src,
ieee_hints->src_pan, packet.src64, pinfo->current_proto, pinfo->num);
}
break;
case ZBEE_SEC_KEY_NWK:
if (ieee_hints) {
/* Map this long address with the ieee short address. */
ieee_hints->map_rec = ieee802154_addr_update(&zbee_nwk_map, ieee_hints->src16,
ieee_hints->src_pan, packet.src64, pinfo->current_proto, pinfo->num);
}
break;
/* We ignore the extended source addresses used to encrypt payloads with these
* types of keys, because they can emerge from APS tunnels created by nodes whose
* short address is not recorded in the packet. */
case ZBEE_SEC_KEY_TRANSPORT:
case ZBEE_SEC_KEY_LOAD:
break;
}
}
#endif
offset += 8;
}
else {
/* 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
proto_tree_add_expert(sec_tree, pinfo, &ei_zbee_sec_extended_source_unknown, tvb, 0, 0);
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
proto_tree_add_expert(sec_tree, pinfo, &ei_zbee_sec_extended_source_unknown, tvb, 0, 0);
break;
}
}
if (packet.key_id == ZBEE_SEC_KEY_NWK) {
/* Get and display the key sequence number. */
packet.key_seqno = tvb_get_guint8(tvb, offset);
proto_tree_add_uint(sec_tree, hf_zbee_sec_key_seqno, tvb, offset, 1, packet.key_seqno);
offset += 1;
}
/* Determine the length of the MIC. */
switch (packet.level) {
case ZBEE_SEC_ENC:
case ZBEE_SEC_NONE:
default:
mic_len=0;
break;
case ZBEE_SEC_ENC_MIC32:
case ZBEE_SEC_MIC32:
mic_len=4;
break;
case ZBEE_SEC_ENC_MIC64:
case ZBEE_SEC_MIC64:
mic_len=8;
break;
case ZBEE_SEC_ENC_MIC128:
case ZBEE_SEC_MIC128:
mic_len=16;
break;
} /* switch */
/* Get and display the MIC. */
if (mic_len) {
/* Display the MIC. */
proto_tree_add_item(sec_tree, hf_zbee_sec_mic, tvb, (gint)(tvb_captured_length(tvb)-mic_len),
mic_len, ENC_NA);
}
/* Check for null payload. */
payload_len = tvb_reported_length_remaining(tvb, offset+mic_len);
if (payload_len == 0)
return NULL;
/**********************************************
* Perform Security Operations on the Frame *
**********************************************
*/
if ((packet.level == ZBEE_SEC_NONE) ||
(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_length(tvb, offset, payload_len);
}
/* Have we captured all the payload? */
if (tvb_captured_length_remaining(tvb, offset+mic_len) < payload_len) {
/*
* No - don't try to decrypt it.
*
* XXX - it looks as if the decryption code is assuming we have the
* MIC, which won't be the case if the packet was cut short. Is
* that in fact that case, or can we still make this work with a
* partially-captured packet?
*/
/* Add expert info. */
expert_add_info(pinfo, sec_tree, &ei_zbee_sec_encrypted_payload_sliced);
/* Create a buffer for the undecrypted payload. */
payload_tvb = tvb_new_subset_length(tvb, offset, payload_len);
/* Dump the payload to the data dissector. */
call_data_dissector(payload_tvb, pinfo, tree);
/* Couldn't decrypt, so return NULL. */
return NULL;
}
/* Allocate memory to decrypt the payload into. */
dec_buffer = (guint8 *)wmem_alloc(pinfo->pool, payload_len);
decrypted = FALSE;
if ( packet.src64 ) {
if (pinfo->fd->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;
}
}
} /* ( !pinfo->fd->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 = (GSList **)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 = (key_record_t *)(GSList_i->data);
break;
default:
key_rec = nwk_hints->link = (key_record_t *)(GSList_i->data);
break;
}
} else {
GSList_i = g_slist_next(GSList_i);
}
}
}
/* 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 = (key_record_t *)(GSList_i->data);
break;
default:
key_rec = nwk_hints->link = (key_record_t *)(GSList_i->data);
break;
}
} else {
GSList_i = g_slist_next(GSList_i);
}
}
}
} /* ( ! pinfo->fd->visited ) */
} /* ( packet.src64 ) */
if ( decrypted ) {
if ( tree && key_rec ) {
key_item = proto_tree_add_bytes(sec_tree, hf_zbee_sec_key, tvb, 0, ZBEE_SEC_CONST_KEYSIZE, key_rec->key);
proto_item_set_generated(key_item);
if ( key_rec->frame_num == ZBEE_SEC_PC_KEY ) {
ti = proto_tree_add_string(sec_tree, hf_zbee_sec_decryption_key, tvb, 0, 0, key_rec->label);
} else {
ti = proto_tree_add_uint(sec_tree, hf_zbee_sec_key_origin, tvb, 0, 0,
key_rec->frame_num);
}
proto_item_set_generated(ti);
}
/* 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);
add_new_data_source(pinfo, payload_tvb, "Decrypted ZigBee Payload");
/* Done! */
return payload_tvb;
}
/* Add expert info. */
expert_add_info(pinfo, sec_tree, &ei_zbee_sec_encrypted_payload);
/* Create a buffer for the undecrypted payload. */
payload_tvb = tvb_new_subset_length(tvb, offset, payload_len);
/* Dump the payload to the data dissector. */
call_data_dissector(payload_tvb, pinfo, tree);
/* Couldn't decrypt, so return NULL. */
return NULL;
} /* dissect_zbee_secure */
/*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
* zbee_security_packet *packet - Security information.
* gchar *nonce - Nonce Buffer.
* RETURNS
* void
*---------------------------------------------------------------
*/
static void
zbee_sec_make_nonce(zbee_security_packet *packet, guint8 *nonce)
{
/* First 8 bytes are the extended source address (little endian). */
phtole64(nonce, packet->src64);
nonce += 8;
/* Next 4 bytes are the frame counter (little endian). */
phtole32(nonce, packet->counter);
nonce += 4;
/* Next byte is the security control field. */
*(nonce) = packet->control;
} /* zbee_sec_make_nonce */
/*FUNCTION:------------------------------------------------------
* NAME
* zbee_sec_ccm_decrypt
* DESCRIPTION
* Performs the Reverse CCM* Transformation (specified in
* section A.3 of ZigBee Specification (053474r17).
*
* The length of parameter c (l(c)) is derived from the length
* of the payload and length of the MIC tag. Input buffer a
* will NOT be modified.
*
* When l_m is 0, then there is no payload to encrypt (ie: the
* payload is in plaintext), and this function will perform
* MIC verification only. When l_m is 0, m may be NULL.
* PARAMETERS
* gchar *key - ZigBee Security Key (must be ZBEE_SEC_CONST_KEYSIZE) in length.
* gchar *nonce - ZigBee CCM* Nonce (must be ZBEE_SEC_CONST_NONCE_LEN) in length.
* gchar *a - CCM* Parameter a (must be l(a) in length). Additional data covered
* by the authentication process.
* gchar *c - CCM* Parameter c (must be l(c) = l(m) + M in length). Encrypted
* payload + encrypted authentication tag U.
* gchar *m - CCM* Output (must be l(m) in length). Decrypted Payload.
* guint l_a - l(a), length of CCM* parameter a.
* guint l_m - l(m), length of expected payload.
* guint M - M, length of CCM* authentication tag.
* RETURNS
* gboolean - TRUE if successful.
*---------------------------------------------------------------
*/
gboolean
zbee_sec_ccm_decrypt(const gchar *key, /* Input */
const gchar *nonce, /* Input */
const gchar *a, /* Input */
const gchar *c, /* Input */
gchar *m, /* Output */
guint l_a, /* sizeof(a) */
guint l_m, /* sizeof(m) */
guint M) /* sizeof(c) - sizeof(m) = sizeof(MIC) */
{
guint8 cipher_in[ZBEE_SEC_CONST_BLOCKSIZE];
guint8 cipher_out[ZBEE_SEC_CONST_BLOCKSIZE];
guint8 decrypted_mic[ZBEE_SEC_CONST_BLOCKSIZE];
guint i, j;
/* Cipher Instance. */
gcry_cipher_hd_t cipher_hd;
/* Sanity-Check. */
if (M > ZBEE_SEC_CONST_BLOCKSIZE) return FALSE;
/*
* The CCM* counter is L bytes in length, ensure that the payload
* isn't long enough to overflow it.
*/
if ((1 + (l_a/ZBEE_SEC_CONST_BLOCKSIZE)) > (1<<(ZBEE_SEC_CONST_L*8))) return FALSE;
/******************************************************
* Step 1: Encryption/Decryption Transformation
******************************************************
*/
/* Create the CCM* counter block A0 */
memset(cipher_in, 0, ZBEE_SEC_CONST_BLOCKSIZE);
cipher_in[0] = ZBEE_SEC_CCM_FLAG_L;
memcpy(cipher_in + 1, nonce, ZBEE_SEC_CONST_NONCE_LEN);
/*
* The encryption/decryption process of CCM* works in CTR mode. Open a CTR
* mode cipher for this phase. NOTE: The 'counter' part of the CCM* counter
* block is the last two bytes, and is big-endian.
*/
if (gcry_cipher_open(&cipher_hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, 0)) {
return FALSE;
}
/* Set the Key. */
if (gcry_cipher_setkey(cipher_hd, key, ZBEE_SEC_CONST_KEYSIZE)) {
gcry_cipher_close(cipher_hd);
return FALSE;
}
/* Set the counter. */
if (gcry_cipher_setctr(cipher_hd, cipher_in, ZBEE_SEC_CONST_BLOCKSIZE)) {
gcry_cipher_close(cipher_hd);
return FALSE;
}
/*
* Copy the MIC into the stack buffer. We need to feed the cipher a full
* block when decrypting the MIC (so that the payload starts on the second
* block). However, the MIC may be less than a full block so use a fixed
* size buffer to store the MIC, letting the CTR cipher overstep the MIC
* if need be.
*/
memset(decrypted_mic, 0, ZBEE_SEC_CONST_BLOCKSIZE);
memcpy(decrypted_mic, c + l_m, M);
/* Encrypt/Decrypt the MIC in-place. */
if (gcry_cipher_encrypt(cipher_hd, decrypted_mic, ZBEE_SEC_CONST_BLOCKSIZE, decrypted_mic, ZBEE_SEC_CONST_BLOCKSIZE)) {
gcry_cipher_close(cipher_hd);
return FALSE;
}
/* Encrypt/Decrypt the payload. */
if (gcry_cipher_encrypt(cipher_hd, m, l_m, c, l_m)) {
gcry_cipher_close(cipher_hd);
return FALSE;
}
/* Done with the CTR Cipher. */
gcry_cipher_close(cipher_hd);
/******************************************************
* Step 3: Authentication Transformation
******************************************************
*/
if (M == 0) {
/* There is no authentication tag. We're done! */
return TRUE;
}
/*
* The authentication process in CCM* operates in CBC-MAC mode, but
* unfortunately, the input to the CBC-MAC process needs some substantial
* transformation and padding before we can feed it into the CBC-MAC
* algorithm. Instead we will operate in ECB mode and perform the
* transformation and padding on the fly.
*
* I also think that libgcrypt requires the input to be memory-aligned
* when using CBC-MAC mode, in which case can't just feed it with data
* from the packet buffer. All things considered it's just a lot easier
* to use ECB mode and do CBC-MAC manually.
*/
/* Re-open the cipher in ECB mode. */
if (gcry_cipher_open(&cipher_hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB, 0)) {
return FALSE;
}
/* Re-load the key. */
if (gcry_cipher_setkey(cipher_hd, key, ZBEE_SEC_CONST_KEYSIZE)) {
gcry_cipher_close(cipher_hd);
return FALSE;
}
/* Generate the first cipher block B0. */
cipher_in[0] = ZBEE_SEC_CCM_FLAG_M(M) |
ZBEE_SEC_CCM_FLAG_ADATA(l_a) |
ZBEE_SEC_CCM_FLAG_L;
memcpy(cipher_in+sizeof(gchar), nonce, ZBEE_SEC_CONST_NONCE_LEN);
for (i=0;i<ZBEE_SEC_CONST_L; i++) {
cipher_in[(ZBEE_SEC_CONST_BLOCKSIZE-1)-i] = (l_m >> (8*i)) & 0xff;
} /* for */
/* Generate the first cipher block, X1 = E(Key, 0^128 XOR B0). */
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;
}
/*
* We avoid mallocing() big chunks of memory by recycling small stack
* buffers for the encryption process. Throughout this process, j is always
* pointed to the position within the current buffer.
*/
j = 0;
/* AuthData = L(a) || a || Padding || m || Padding
* Where L(a) =
* - an empty string if l(a) == 0.
* - 2-octet encoding of l(a) if 0 < l(a) < (2^16 - 2^8)
* - 0xff || 0xfe || 4-octet encoding of l(a) if (2^16 - 2^8) <= l(a) < 2^32
* - 0xff || 0xff || 8-octet encoding of l(a)
* But for ZigBee, the largest packet size we should ever see is 2^7, so we
* are only really concerned with the first two cases.
*
* To generate the MIC tag CCM* operates similar to CBC-MAC mode. Each block
* of AuthData is XOR'd with the last block of cipher output to produce the
* next block of cipher output. Padding sections have the minimum non-negative
* length such that the padding ends on a block boundary. Padded bytes are 0.
*/
if (l_a > 0) {
/* Process L(a) into the cipher block. */
cipher_in[j] = cipher_out[j] ^ ((l_a >> 8) & 0xff);
j++;
cipher_in[j] = cipher_out[j] ^ ((l_a >> 0) & 0xff);
j++;
/* Process a into the cipher block. */
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)) {
gcry_cipher_close(cipher_hd);
return FALSE;
}
/* Reset j to point back to the start of the new cipher block. */
j = 0;
}
/* Cipher in = cipher_out ^ a */
cipher_in[j] = cipher_out[j] ^ a[i];
} /* for */
/* Process padding into the cipher block. */
for (;j<ZBEE_SEC_CONST_BLOCKSIZE;j++)
cipher_in[j] = cipher_out[j];
}
/* Process m into the cipher block. */
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)) {
gcry_cipher_close(cipher_hd);
return FALSE;
}
/* Reset j to point back to the start of the new cipher block. */
j = 0;
}
/* Cipher in = cipher out ^ m */
cipher_in[j] = cipher_out[j] ^ m[i];
} /* for */
/* Padding. */
for (;j<ZBEE_SEC_CONST_BLOCKSIZE;j++)
cipher_in[j] = cipher_out[j];
/* Generate the last cipher block, which will be the MIC tag. */
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;
}
/* Done with the Cipher. */
gcry_cipher_close(cipher_hd);
/* Compare the MIC's */
return (memcmp(cipher_out, decrypted_mic, M) == 0);
} /* zbee_ccm_decrypt */
/*FUNCTION:------------------------------------------------------
* NAME
* zbee_sec_hash
* DESCRIPTION
* ZigBee Cryptographic Hash Function, described in ZigBee
* specification sections B.1.3 and B.6.
*
* This is a Matyas-Meyer-Oseas hash function using the AES-128
* cipher. We use the ECB mode of libgcrypt to get a raw block
* cipher.
*
* Input may be any length, and the output must be exactly 1-block in length.
*
* Implements the function:
* Hash(text) = Hash[t];
* Hash[0] = 0^(blocksize).
* Hash[i] = E(Hash[i-1], M[i]) XOR M[j];
* M[i] = i'th block of text, with some padding and flags concatenated.
* PARAMETERS
* guint8 * input - Hash Input (any length).
* guint8 input_len - Hash Input Length.
* guint8 * output - Hash Output (exactly one block in length).
* RETURNS
* void
*---------------------------------------------------------------
*/
static void
zbee_sec_hash(guint8 *input, guint input_len, guint8 *output)
{
guint8 cipher_in[ZBEE_SEC_CONST_BLOCKSIZE];
guint i, j;
/* Cipher Instance. */
gcry_cipher_hd_t cipher_hd;
/* Clear the first hash block (Hash0). */
memset(output, 0, ZBEE_SEC_CONST_BLOCKSIZE);
/* Create the cipher instance in ECB mode. */
if (gcry_cipher_open(&cipher_hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB, 0)) {
return; /* Failed. */
}
/* Create the subsequent hash blocks using the formula: Hash[i] = E(Hash[i-1], M[i]) XOR M[i]
*
* because we can't guarantee that M will be exactly a multiple of the
* block size, we will need to copy it into local buffers and pad it.
*
* Note that we check for the next cipher block at the end of the loop
* rather than the start. This is so that if the input happens to end
* on a block boundary, the next cipher block will be generated for the
* start of the padding to be placed into.
*/
i = 0;
j = 0;
while (i<input_len) {
/* Copy data into the cipher input. */
cipher_in[j++] = input[i++];
/* Check if this cipher block is done. */
if (j >= ZBEE_SEC_CONST_BLOCKSIZE) {
/* We have reached the end of this block. Process it with the
* cipher, note that the Key input to the cipher is actually
* the previous hash block, which we are keeping in output.
*/
(void)gcry_cipher_setkey(cipher_hd, output, ZBEE_SEC_CONST_BLOCKSIZE);
(void)gcry_cipher_encrypt(cipher_hd, output, ZBEE_SEC_CONST_BLOCKSIZE, cipher_in, ZBEE_SEC_CONST_BLOCKSIZE);
/* Now we have to XOR the input into the hash block. */
for (j=0;j<ZBEE_SEC_CONST_BLOCKSIZE;j++) output[j] ^= cipher_in[j];
/* Reset j to start again at the beginning at the next block. */
j = 0;
}
} /* for */
/* Need to append the bit '1', followed by '0' padding long enough to end
* the hash input on a block boundary. However, because 'n' is 16, and 'l'
* will be a multiple of 8, the padding will be >= 7-bits, and we can just
* append the byte 0x80.
*/
cipher_in[j++] = 0x80;
/* Pad with '0' until the the current block is exactly 'n' bits from the
* end.
*/
while (j!=(ZBEE_SEC_CONST_BLOCKSIZE-2)) {
if (j >= ZBEE_SEC_CONST_BLOCKSIZE) {
/* We have reached the end of this block. Process it with the
* cipher, note that the Key input to the cipher is actually
* the previous hash block, which we are keeping in output.
*/
(void)gcry_cipher_setkey(cipher_hd, output, ZBEE_SEC_CONST_BLOCKSIZE);
(void)gcry_cipher_encrypt(cipher_hd, output, ZBEE_SEC_CONST_BLOCKSIZE, cipher_in, ZBEE_SEC_CONST_BLOCKSIZE);
/* Now we have to XOR the input into the hash block. */
for (j=0;j<ZBEE_SEC_CONST_BLOCKSIZE;j++) output[j] ^= cipher_in[j];
/* Reset j to start again at the beginning at the next block. */
j = 0;
}
/* Pad the input with 0. */
cipher_in[j++] = 0x00;
} /* while */
/* Add the 'n'-bit representation of 'l' to the end of the block. */
cipher_in[j++] = ((input_len * 8) >> 8) & 0xff;
cipher_in[j] = ((input_len * 8) >> 0) & 0xff;
/* Process the last cipher block. */
(void)gcry_cipher_setkey(cipher_hd, output, ZBEE_SEC_CONST_BLOCKSIZE);
(void)gcry_cipher_encrypt(cipher_hd, output, ZBEE_SEC_CONST_BLOCKSIZE, cipher_in, ZBEE_SEC_CONST_BLOCKSIZE);
/* XOR the last input block back into the cipher output to get the hash. */
for (j=0;j<ZBEE_SEC_CONST_BLOCKSIZE;j++) output[j] ^= cipher_in[j];
/* Cleanup the cipher. */
gcry_cipher_close(cipher_hd);
/* Done */
} /* zbee_sec_hash */
/*FUNCTION:------------------------------------------------------
* NAME
* zbee_sec_key_hash
* DESCRIPTION
* ZigBee Keyed Hash Function. Described in ZigBee specification
* section B.1.4, and in FIPS Publication 198. Strictly speaking
* there is nothing about the Keyed Hash Function which restricts
* it to only a single byte input, but that's all ZigBee ever uses.
*
* This function implements the hash function:
* Hash(Key, text) = H((Key XOR opad) || H((Key XOR ipad) || text));
* ipad = 0x36 repeated.
* opad = 0x5c repeated.
* H() = ZigBee Cryptographic Hash (B.1.3 and B.6).
* 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.
* guint8 *hash_out - buffer into which the key-hashed output is placed
* RETURNS
* void
*---------------------------------------------------------------
*/
static void
zbee_sec_key_hash(guint8 *key, guint8 input, guint8 *hash_out)
{
guint8 hash_in[2*ZBEE_SEC_CONST_BLOCKSIZE];
int i;
static const guint8 ipad = 0x36;
static const guint8 opad = 0x5c;
/* Copy the key into hash_in and XOR with opad to form: (Key XOR opad) */
for (i=0; i<ZBEE_SEC_CONST_KEYSIZE; i++) hash_in[i] = key[i] ^ opad;
/* Copy the Key into hash_out and XOR with ipad to form: (Key XOR ipad) */
for (i=0; i<ZBEE_SEC_CONST_KEYSIZE; i++) hash_out[i] = key[i] ^ ipad;
/* Append the input byte to form: (Key XOR ipad) || text. */
hash_out[ZBEE_SEC_CONST_BLOCKSIZE] = input;
/* Hash the contents of hash_out and append the contents to hash_in to
* form: (Key XOR opad) || H((Key XOR ipad) || text).
*/
zbee_sec_hash(hash_out, ZBEE_SEC_CONST_BLOCKSIZE+1, hash_in+ZBEE_SEC_CONST_BLOCKSIZE);
/* Hash the contents of hash_in to get the final result. */
zbee_sec_hash(hash_in, 2*ZBEE_SEC_CONST_BLOCKSIZE, hash_out);
} /* zbee_sec_key_hash */
/**
*Add NWK or APS key into NWK keyring
*
*@param pinfo pointer to packet information fields
*@param key APS or NWK key
*/
void zbee_sec_add_key_to_keyring(packet_info *pinfo, const guint8 *key)
{
GSList **nwk_keyring;
key_record_t key_record;
zbee_nwk_hints_t *nwk_hints;
/* Update the key ring for this pan */
if ( !pinfo->fd->visited && (nwk_hints = (zbee_nwk_hints_t *)p_get_proto_data(wmem_file_scope(), pinfo,
proto_get_id_by_filter_name(ZBEE_PROTOABBREV_NWK), 0))) {
nwk_keyring = (GSList **)g_hash_table_lookup(zbee_table_nwk_keyring, &nwk_hints->src_pan);
if ( !nwk_keyring ) {
nwk_keyring = (GSList **)g_malloc0(sizeof(GSList*));
g_hash_table_insert(zbee_table_nwk_keyring,
g_memdup2(&nwk_hints->src_pan, sizeof(nwk_hints->src_pan)), nwk_keyring);
}
if ( nwk_keyring ) {
if ( !*nwk_keyring ||
memcmp( ((key_record_t *)((GSList *)(*nwk_keyring))->data)->key, key,
ZBEE_APS_CMD_KEY_LENGTH) ) {
/* Store a new or different key in the key ring */
key_record.frame_num = pinfo->num;
key_record.label = NULL;
memcpy(&key_record.key, key, ZBEE_APS_CMD_KEY_LENGTH);
*nwk_keyring = g_slist_prepend(*nwk_keyring, g_memdup2(&key_record, sizeof(key_record_t)));
}
}
}
} /* nwk_add_key_to_keyring */
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/