/* packet-zbee-security.c * Dissector helper routines for encrypted ZigBee frames. * By Owen Kirby ; portions by Fred Fierling * Copyright 2009 Exegin Technologies Limited * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ /* Include Files */ #include "config.h" #include #include #include #include #include #include /* 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 #include #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=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> (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=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) { /* 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) { /* 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= 7-bits, and we can just * append the byte 0x80. */ cipher_in[j++] = 0x80; /* Pad with '0' until 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> 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;jfd->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: */