-/* Routines for LTE PDCP
+/* packet-pdcp-lte.c
+ * Routines for LTE PDCP
*
* Martin Mathieson
*
- * $Id$
- *
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
#include "config.h"
-#include <string.h>
-#include <glib.h>
#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/expert.h>
-#include <epan/addr_resolv.h>
-#include <epan/wmem/wmem.h>
-
-#ifdef HAVE_LIBGCRYPT
#include <epan/uat.h>
#include <wsutil/wsgcrypt.h>
-#endif /* HAVE_LIBGCRYPT */
+
+/* Define this symbol if you have a working implementation of SNOW3G f8() and f9() available.
+ Note that the use of this algorithm is restricted, and that an administrative charge
+ may be applicable if you use it (see e.g. http://www.gsma.com/technicalprojects/fraud-security/security-algorithms).
+ A version of Wireshark with this enabled would not be distributable. */
+/* #define HAVE_SNOW3G */
#include "packet-rlc-lte.h"
#include "packet-pdcp-lte.h"
/* TODO:
- - More deciphering. Next steps are:
- - separate preferences to control signalling/user-plane decryption?
- - Verify MAC authentication bytes for supported protocol(s)?
+ - Decipher even if sequence analysis isn't 'OK'?
+ - know SN, but might be unsure about HFN.
+ - Speed up AES decryption by keeping the crypt handle around for the channel
+ (like ESP decryption in IPSEC dissector)
- Add Relay Node user plane data PDU dissection
*/
static int hf_pdcp_lte_reserved4 = -1;
static int hf_pdcp_lte_fms2 = -1;
static int hf_pdcp_lte_bitmap = -1;
+static int hf_pdcp_lte_bitmap_byte = -1;
/* Sequence Analysis */
static int hf_pdcp_lte_security_bearer = -1;
static int hf_pdcp_lte_security_direction = -1;
static int hf_pdcp_lte_security_count = -1;
-static int hf_pdcp_lte_security_key = -1;
+static int hf_pdcp_lte_security_cipher_key = -1;
+static int hf_pdcp_lte_security_integrity_key = -1;
static expert_field ei_pdcp_lte_reserved_bits_not_zero = EI_INIT;
static expert_field ei_pdcp_lte_sequence_analysis_sn_repeated = EI_INIT;
static expert_field ei_pdcp_lte_sequence_analysis_sn_missing = EI_INIT;
+static expert_field ei_pdcp_lte_digest_wrong = EI_INIT;
-#ifdef HAVE_LIBGCRYPT
/*-------------------------------------
* UAT for UE Keys
*-------------------------------------
/* UAT entry structure. */
typedef struct {
guint16 ueid;
- gchar *rrcKey;
- gchar *upKey;
+ gchar *rrcCipherKeyString;
+ gchar *upCipherKeyString;
+ gchar *rrcIntegrityKeyString;
+
+ guint8 rrcCipherBinaryKey[16];
+ gboolean rrcCipherKeyOK;
+ guint8 upCipherBinaryKey[16];
+ gboolean upCipherKeyOK;
+ guint8 rrcIntegrityBinaryKey[16];
+ gboolean rrcIntegrityKeyOK;
} uat_ue_keys_record_t;
static uat_ue_keys_record_t *uat_ue_keys_records = NULL;
+/* Entries added by UAT */
static uat_t * ue_keys_uat = NULL;
static guint num_ue_keys_uat = 0;
+/* Convert an ascii hex character into a digit. Should only be given valid
+ hex ascii characters */
+static guchar hex_ascii_to_binary(gchar c)
+{
+ if ((c >= '0') && (c <= '9')) {
+ return c - '0';
+ }
+ else if ((c >= 'a') && (c <= 'f')) {
+ return 10 + c - 'a';
+ }
+ else if ((c >= 'A') && (c <= 'F')) {
+ return 10 + c - 'A';
+ }
+ else
+ return 0;
+}
+
static void* uat_ue_keys_record_copy_cb(void* n, const void* o, size_t siz _U_) {
uat_ue_keys_record_t* new_rec = (uat_ue_keys_record_t *)n;
const uat_ue_keys_record_t* old_rec = (const uat_ue_keys_record_t *)o;
new_rec->ueid = old_rec->ueid;
- new_rec->rrcKey = (old_rec->rrcKey) ? g_strdup(old_rec->rrcKey) : NULL;
- new_rec->upKey = (old_rec->upKey) ? g_strdup(old_rec->upKey) : NULL;
+ new_rec->rrcCipherKeyString = (old_rec->rrcCipherKeyString) ? g_strdup(old_rec->rrcCipherKeyString) : NULL;
+ new_rec->upCipherKeyString = (old_rec->upCipherKeyString) ? g_strdup(old_rec->upCipherKeyString) : NULL;
+ new_rec->rrcIntegrityKeyString = (old_rec->rrcIntegrityKeyString) ? g_strdup(old_rec->rrcIntegrityKeyString) : NULL;
return new_rec;
}
+/* If raw_string is a valid key, set check_string & return TRUE */
+static gboolean check_valid_key_sring(const char* raw_string, char* checked_string)
+{
+ guint n;
+ guint written = 0;
+ guint length = (gint)strlen(raw_string);
+
+ /* Can't be valid if not long enough. */
+ if (length < 32) {
+ return FALSE;
+ }
+
+ for (n=0; (n < length) && (written < 32); n++) {
+ char c = raw_string[n];
+
+ /* Skipping past allowed 'padding' characters */
+ if ((c == ' ') || (c == '-')) {
+ continue;
+ }
+
+ /* Other characters must be hex digits, otherwise string is invalid */
+ if (((c >= '0') && (c <= '9')) ||
+ ((c >= 'a') && (c <= 'f')) ||
+ ((c >= 'A') && (c <= 'F'))) {
+ checked_string[written++] = c;
+ }
+ else {
+ return FALSE;
+ }
+ }
+
+ /* Must have found exactly 32 hex ascii chars for 16-byte key */
+ return (written == 32);
+}
+
+static void update_key_from_string(const char *stringKey, guint8 *binaryKey, gboolean *pKeyOK)
+{
+ int n;
+ char cleanString[32];
+
+ if (!check_valid_key_sring(stringKey, cleanString)) {
+ *pKeyOK = FALSE;
+ }
+ else {
+ for (n=0; n < 32; n += 2) {
+ binaryKey[n/2] = (hex_ascii_to_binary(cleanString[n]) << 4) +
+ hex_ascii_to_binary(cleanString[n+1]);
+ }
+ *pKeyOK = TRUE;
+ }
+}
+
+/* Update by checking whether the 3 key strings are valid or not, and storing result */
+static gboolean uat_ue_keys_record_update_cb(void* record, char** error _U_) {
+ uat_ue_keys_record_t* rec = (uat_ue_keys_record_t *)record;
+
+ /* Check and convert RRC key */
+ update_key_from_string(rec->rrcCipherKeyString, rec->rrcCipherBinaryKey, &rec->rrcCipherKeyOK);
+
+ /* Check and convert User-plane key */
+ update_key_from_string(rec->upCipherKeyString, rec->upCipherBinaryKey, &rec->upCipherKeyOK);
+
+ /* Check and convert Integrity key */
+ update_key_from_string(rec->rrcIntegrityKeyString, rec->rrcIntegrityBinaryKey, &rec->rrcIntegrityKeyOK);
+
+ return TRUE;
+}
+
+/* Free heap parts of record */
static void uat_ue_keys_record_free_cb(void*r) {
uat_ue_keys_record_t* rec = (uat_ue_keys_record_t*)r;
- g_free(rec->rrcKey);
- g_free(rec->upKey);
+ g_free(rec->rrcCipherKeyString);
+ g_free(rec->upCipherKeyString);
+ g_free(rec->rrcIntegrityKeyString);
}
UAT_DEC_CB_DEF(uat_ue_keys_records, ueid, uat_ue_keys_record_t)
-UAT_CSTRING_CB_DEF(uat_ue_keys_records, rrcKey, uat_ue_keys_record_t)
-UAT_CSTRING_CB_DEF(uat_ue_keys_records, upKey, uat_ue_keys_record_t)
+UAT_CSTRING_CB_DEF(uat_ue_keys_records, rrcCipherKeyString, uat_ue_keys_record_t)
+UAT_CSTRING_CB_DEF(uat_ue_keys_records, upCipherKeyString, uat_ue_keys_record_t)
+UAT_CSTRING_CB_DEF(uat_ue_keys_records, rrcIntegrityKeyString, uat_ue_keys_record_t)
-static gboolean global_pdcp_decipher_signalling = FALSE;
-static gboolean global_pdcp_decipher_userplane = FALSE;
-#endif
+/* Also supporting a hash table with entries from these functions */
+
+/* Table from ueid -> uat_ue_keys_record_t* */
+static GHashTable *pdcp_security_key_hash = NULL;
+
+
+void set_pdcp_lte_rrc_ciphering_key(guint16 ueid, const char *key)
+{
+ /* Get or create struct for this UE */
+ uat_ue_keys_record_t *key_record = (uat_ue_keys_record_t*)g_hash_table_lookup(pdcp_security_key_hash,
+ GUINT_TO_POINTER((guint)ueid));
+ if (key_record == NULL) {
+ /* Create and add to table */
+ key_record = wmem_new0(wmem_file_scope(), uat_ue_keys_record_t);
+ key_record->ueid = ueid;
+ g_hash_table_insert(pdcp_security_key_hash, GUINT_TO_POINTER((guint)ueid), key_record);
+ }
+
+ /* Check and convert RRC key */
+ key_record->rrcCipherKeyString = g_strdup(key);
+ update_key_from_string(key_record->rrcCipherKeyString, key_record->rrcCipherBinaryKey, &key_record->rrcCipherKeyOK);}
+
+void set_pdcp_lte_rrc_integrity_key(guint16 ueid, const char *key)
+{
+ /* Get or create struct for this UE */
+ uat_ue_keys_record_t *key_record = (uat_ue_keys_record_t*)g_hash_table_lookup(pdcp_security_key_hash,
+ GUINT_TO_POINTER((guint)ueid));
+ if (key_record == NULL) {
+ /* Create and add to table */
+ key_record = wmem_new0(wmem_file_scope(), uat_ue_keys_record_t);
+ key_record->ueid = ueid;
+ g_hash_table_insert(pdcp_security_key_hash, GUINT_TO_POINTER((guint)ueid), key_record);
+ }
+
+ /* Check and convert RRC integrity key */
+ key_record->rrcIntegrityKeyString = g_strdup(key);
+ update_key_from_string(key_record->rrcIntegrityKeyString, key_record->rrcIntegrityBinaryKey, &key_record->rrcIntegrityKeyOK);
+}
+
+void set_pdcp_lte_up_ciphering_key(guint16 ueid, const char *key)
+{
+ /* Get or create struct for this UE */
+ uat_ue_keys_record_t *key_record = (uat_ue_keys_record_t*)g_hash_table_lookup(pdcp_security_key_hash,
+ GUINT_TO_POINTER((guint)ueid));
+ if (key_record == NULL) {
+ /* Create and add to table */
+ key_record = wmem_new0(wmem_file_scope(), uat_ue_keys_record_t);
+ key_record->ueid = ueid;
+ g_hash_table_insert(pdcp_security_key_hash, GUINT_TO_POINTER((guint)ueid), key_record);
+ }
+
+ /* Check and convert UP key */
+ key_record->upCipherKeyString = g_strdup(key);
+ update_key_from_string(key_record->upCipherKeyString, key_record->upCipherBinaryKey, &key_record->upCipherKeyOK);
+}
+
+
+/* Preference settings for deciphering and integrity checking. Currently all default to off */
+static gboolean global_pdcp_decipher_signalling = TRUE;
+static gboolean global_pdcp_decipher_userplane = FALSE; /* Can be slow, so default to FALSE */
+static gboolean global_pdcp_check_integrity = TRUE;
+
+/* Use these values where we know the keys but may have missed the algorithm,
+ e.g. when handing over and RRCReconfigurationRequest goes to target cell only */
+static enum security_ciphering_algorithm_e global_default_ciphering_algorithm = eea0;
+static enum security_integrity_algorithm_e global_default_integrity_algorithm = eia0;
+
static const value_string direction_vals[] =
{
{ 0, "EIA0" },
{ 1, "EIA1" },
{ 2, "EIA2" },
+ { 3, "EIA3" },
{ 0, NULL }
};
{ 0, "EEA0" },
{ 1, "EEA1" },
{ 2, "EEA2" },
+ { 3, "EEA3" },
{ 0, NULL }
};
Maps key -> status */
static GHashTable *pdcp_sequence_analysis_channel_hash = NULL;
-/* Equal keys */
-static gint pdcp_channel_equal(gconstpointer v, gconstpointer v2)
-{
- /* Key fits in 4 bytes, so just compare pointers! */
- return (v == v2);
-}
-
-/* Compute a hash value for a given key. */
-static guint pdcp_channel_hash_func(gconstpointer v)
-{
- /* Just use pointer, as the fields are all in this value */
- return GPOINTER_TO_UINT(v);
-}
-
/* Hash table types & functions for frame reports */
/* Gather together security settings in order to be able to do deciphering */
typedef struct pdu_security_settings_t
{
- gboolean valid;
enum security_ciphering_algorithm_e ciphering;
- const gchar *key;
+ enum security_integrity_algorithm_e integrity;
+ guint8* cipherKey;
+ guint8* integrityKey;
+ gboolean cipherKeyValid;
+ gboolean integrityKeyValid;
guint32 count;
guint8 bearer;
guint8 direction;
} pdu_security_settings_t;
+static uat_ue_keys_record_t* look_up_keys_record(guint16 ueid)
+{
+ unsigned int record_id;
+ /* Try hash table first */
+ uat_ue_keys_record_t* key_record = (uat_ue_keys_record_t*)g_hash_table_lookup(pdcp_security_key_hash,
+ GUINT_TO_POINTER((guint)ueid));
+ if (key_record != NULL) {
+ return key_record;
+ }
+
+ /* Else look up UAT entries */
+ for (record_id=0; record_id < num_ue_keys_uat; record_id++) {
+ if (uat_ue_keys_records[record_id].ueid == ueid) {
+ return &uat_ue_keys_records[record_id];
+ }
+ }
+
+ /* No match at all - return NULL */
+ return NULL;
+}
+
/* Add to the tree values associated with sequence analysis for this frame */
static void addChannelSequenceInfo(pdcp_sequence_report_in_frame *p,
pdcp_lte_info *p_pdcp_lte_info,
proto_item *seqnum_ti;
proto_item *ti_expected_sn;
proto_item *ti;
+ uat_ue_keys_record_t *keys_record;
/* Create subtree */
seqnum_ti = proto_tree_add_string_format(tree,
}
/* May also be able to add key inputs to security tree here */
- if (security_tree != NULL) {
+ if ((pdu_security->ciphering != eea0) ||
+ (pdu_security->integrity != eia0)) {
guint32 hfn_multiplier;
guint32 count;
-#if HAVE_LIBGCRYPT
- gchar *key = NULL;
- guint record_id;
-#endif
+ gchar *cipher_key = NULL;
+ gchar *integrity_key = NULL;
+
/* BEARER */
ti = proto_tree_add_uint(security_tree, hf_pdcp_lte_security_bearer,
tvb, 0, 0, p_pdcp_lte_info->channelId-1);
hfn_multiplier = 128;
break;
case PDCP_SN_LENGTH_12_BITS:
- hfn_multiplier = 2048;
+ hfn_multiplier = 4096;
break;
case PDCP_SN_LENGTH_15_BITS:
hfn_multiplier = 32768;
PROTO_ITEM_SET_GENERATED(ti);
pdu_security->count = count;
-#if HAVE_LIBGCRYPT
- /* KEY */
- for (record_id=0; record_id < num_ue_keys_uat; record_id++) {
- if (uat_ue_keys_records[record_id].ueid == p_pdcp_lte_info->ueid) {
- if (p_pdcp_lte_info->plane == SIGNALING_PLANE) {
- key = uat_ue_keys_records[record_id].rrcKey;
+ /* KEY. Look this UE up among UEs that have keys configured */
+ keys_record = look_up_keys_record(p_pdcp_lte_info->ueid);
+ if (keys_record != NULL) {
+ if (p_pdcp_lte_info->plane == SIGNALING_PLANE) {
+ /* Get RRC ciphering key */
+ if (keys_record->rrcCipherKeyOK) {
+ cipher_key = keys_record->rrcCipherKeyString;
+ pdu_security->cipherKey = &(keys_record->rrcCipherBinaryKey[0]);
+ pdu_security->cipherKeyValid = TRUE;
}
- else {
- key = uat_ue_keys_records[record_id].upKey;
+ /* Get RRC integrity key */
+ if (keys_record->rrcIntegrityKeyOK) {
+ integrity_key = keys_record->rrcIntegrityKeyString;
+ pdu_security->integrityKey = &(keys_record->rrcIntegrityBinaryKey[0]);
+ pdu_security->integrityKeyValid = TRUE;
}
-
- if (key != NULL) {
- ti = proto_tree_add_string(security_tree, hf_pdcp_lte_security_key,
- tvb, 0, 0, key);
- PROTO_ITEM_SET_GENERATED(ti);
- pdu_security->key = key;
+ }
+ else {
+ /* Get userplane ciphering key */
+ if (keys_record->upCipherKeyOK) {
+ cipher_key = keys_record->upCipherKeyString;
+ pdu_security->cipherKey = &(keys_record->upCipherBinaryKey[0]);
+ pdu_security->cipherKeyValid = TRUE;
}
}
- }
-#endif
- pdu_security->direction = p_pdcp_lte_info->direction;
+ /* Show keys where known and valid */
+ if (cipher_key != NULL) {
+ ti = proto_tree_add_string(security_tree, hf_pdcp_lte_security_cipher_key,
+ tvb, 0, 0, cipher_key);
+ PROTO_ITEM_SET_GENERATED(ti);
+ }
+ if (integrity_key != NULL) {
+ ti = proto_tree_add_string(security_tree, hf_pdcp_lte_security_integrity_key,
+ tvb, 0, 0, integrity_key);
+ PROTO_ITEM_SET_GENERATED(ti);
+ }
+
+ pdu_security->direction = p_pdcp_lte_info->direction;
+ }
}
break;
static gint pdcp_lte_ueid_frame_hash_equal(gconstpointer v, gconstpointer v2)
{
- ueid_frame_t *ueid_frame_1 = (ueid_frame_t *)v;
- ueid_frame_t *ueid_frame_2 = (ueid_frame_t *)v2;
+ const ueid_frame_t *ueid_frame_1 = (const ueid_frame_t *)v;
+ const ueid_frame_t *ueid_frame_2 = (const ueid_frame_t *)v2;
return ((ueid_frame_1->framenum == ueid_frame_2->framenum) && (ueid_frame_1->ueid == ueid_frame_2->ueid));
}
static guint pdcp_lte_ueid_frame_hash_func(gconstpointer v)
{
- ueid_frame_t *ueid_frame = (ueid_frame_t *)v;
+ const ueid_frame_t *ueid_frame = (const ueid_frame_t *)v;
return ueid_frame->framenum + 100*ueid_frame->ueid;
}
static GHashTable *pdcp_security_result_hash = NULL;
/* Forwad declarations */
static void dissect_pdcp_lte(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
-/* Heuristic dissection */
-static gboolean global_pdcp_lte_heur = FALSE;
-
/* Heuristic dissector looks for supported framing protocol (see wiki page) */
static gboolean dissect_pdcp_lte_heur(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree, void *data _U_)
gboolean infoAlreadySet = FALSE;
gboolean seqnumLengthTagPresent = FALSE;
- /* This is a heuristic dissector, which means we get all the UDP
- * traffic not sent to a known dissector and not claimed by
- * a heuristic dissector called before us!
- */
-
- if (!global_pdcp_lte_heur) {
- return FALSE;
- }
-
/* Do this again on re-dissection to re-discover offset of actual PDU */
/* Needs to be at least as long as:
- fixed header bytes
- tag for data
- at least one byte of PDCP PDU payload */
- if (tvb_length_remaining(tvb, offset) < (gint)(strlen(PDCP_LTE_START_STRING)+3+2)) {
+ if (tvb_captured_length_remaining(tvb, offset) < (gint)(strlen(PDCP_LTE_START_STRING)+3+2)) {
return FALSE;
}
/* Read fixed fields */
p_pdcp_lte_info->no_header_pdu = (gboolean)tvb_get_guint8(tvb, offset++);
p_pdcp_lte_info->plane = (enum pdcp_plane)tvb_get_guint8(tvb, offset++);
+ if (p_pdcp_lte_info->plane == SIGNALING_PLANE) {
+ p_pdcp_lte_info->seqnum_length = PDCP_SN_LENGTH_5_BITS;
+ }
p_pdcp_lte_info->rohc.rohc_compression = (gboolean)tvb_get_guint8(tvb, offset++);
/* Read optional fields */
/* Called from control protocol to configure security algorithms for the given UE */
void set_pdcp_lte_security_algorithms(guint16 ueid, pdcp_security_info_t *security_info)
{
- /* Copy security struct */
- pdcp_security_info_t *p_security = wmem_new(wmem_file_scope(), pdcp_security_info_t);
- *p_security = *security_info;
+ /* Use for this frame so can check integrity on SecurityCommandRequest frame */
+ /* N.B. won't work for internal, non-RRC signalling methods... */
+ pdcp_security_info_t *p_frame_security;
+
+ /* Create or update current settings, by UEID */
+ pdcp_security_info_t* ue_security =
+ (pdcp_security_info_t*)g_hash_table_lookup(pdcp_security_hash,
+ GUINT_TO_POINTER((guint)ueid));
+ if (ue_security == NULL) {
+ /* Copy whole security struct */
+ ue_security = wmem_new(wmem_file_scope(), pdcp_security_info_t);
+ *ue_security = *security_info;
+
+ /* And add into security table */
+ g_hash_table_insert(pdcp_security_hash, GUINT_TO_POINTER((guint)ueid), ue_security);
+ }
+ else {
+ /* Just update existing entry already in table */
+ ue_security->previous_configuration_frame = ue_security->configuration_frame;
+ ue_security->previous_integrity = ue_security->integrity;
+ ue_security->previous_ciphering = ue_security->ciphering;
+
+ ue_security->configuration_frame = security_info->configuration_frame;
+ ue_security->integrity = security_info->integrity;
+ ue_security->ciphering = security_info->ciphering;
+ ue_security->seen_next_ul_pdu = FALSE;
+ }
- /* And add into security table */
- g_hash_table_insert(pdcp_security_hash, GUINT_TO_POINTER((guint)ueid), p_security);
+ /* Also add an entry for this PDU already to use these settings, as otherwise it won't be present
+ when we query it on the first pass. */
+ p_frame_security = wmem_new(wmem_file_scope(), pdcp_security_info_t);
+ *p_frame_security = *ue_security;
+ g_hash_table_insert(pdcp_security_result_hash,
+ get_ueid_frame_hash_key(ueid, ue_security->configuration_frame, TRUE),
+ p_frame_security);
}
-/* TODO: do this better, and only once when UAT updated! */
-static guchar hex_ascii_to_binary(gchar c)
+/* UE failed to process SecurityModeCommand so go back to previous security settings */
+void set_pdcp_lte_security_algorithms_failed(guint16 ueid)
{
- if ((c >= '0') && (c <= '9')) {
- return c - '0';
- }
- else if ((c >= 'a') && (c <= 'f')) {
- return 10 + c - 'a';
- }
- else if ((c >= 'A') && (c <= 'F')) {
- return 10 + c - 'A';
+ /* Look up current state by UEID */
+ pdcp_security_info_t* ue_security =
+ (pdcp_security_info_t*)g_hash_table_lookup(pdcp_security_hash,
+ GUINT_TO_POINTER((guint)ueid));
+ if (ue_security != NULL) {
+ /* TODO: could remove from table if previous_configuration_frame is 0 */
+ /* Go back to previous state */
+ ue_security->configuration_frame = ue_security->previous_configuration_frame;
+ ue_security->integrity = ue_security->previous_integrity;
+ ue_security->ciphering = ue_security->previous_ciphering;
}
- else
- return 0;
}
-#if HAVE_LIBGCRYPT
/* Decipher payload if algorithm is supported and plausible inputs are available */
-tvbuff_t* decipher_payload(tvbuff_t *tvb, packet_info *pinfo, int *offset, pdu_security_settings_t *pdu_security_settings,
- enum pdcp_plane plane, gboolean *deciphered)
+static tvbuff_t *decipher_payload(tvbuff_t *tvb, packet_info *pinfo, int *offset,
+ pdu_security_settings_t *pdu_security_settings,
+ enum pdcp_plane plane, gboolean will_be_deciphered,
+ gboolean *deciphered)
{
- const char *k = pdu_security_settings->key;
- guint8 key[16];
- unsigned char ctr_block[16];
- int n;
- gcry_cipher_hd_t cypher_hd;
- int gcrypt_err;
- guint8* encrypted_data;
- guint8* decrypted_data;
- gint payload_length;
+ guint8* decrypted_data = NULL;
+ gint payload_length = 0;
tvbuff_t *decrypted_tvb;
- /* Nothing to do if no ciphering algorithm was specified */
- if (!pdu_security_settings->valid) {
+ /* Nothing to do if NULL ciphering */
+ if (pdu_security_settings->ciphering == eea0) {
return tvb;
}
- /* Only EEA2 supported at the moment */
- if (pdu_security_settings->ciphering != eea2) {
+ /* Nothing to do if don't have valid cipher key */
+ if (!pdu_security_settings->cipherKeyValid) {
return tvb;
}
- /* Don't decipher if turned off in preferences */
- if (((plane == SIGNALING_PLANE) && !global_pdcp_decipher_signalling) ||
- ((plane == USER_PLANE) && !global_pdcp_decipher_userplane)) {
+ /* Check whether algorithm supported (only drop through and process if we do) */
+ if (pdu_security_settings->ciphering == eea1) {
+#ifndef HAVE_SNOW3G
return tvb;
+#endif
}
-
- /* Key must be present and 16 bytes long */
- if (strlen(k) != 32) {
+ else
+ if (pdu_security_settings->ciphering == eea2) {
+#ifndef HAVE_LIBGCRYPT
return tvb;
+#endif
}
-
- /* And must be able to convert string into binary key */
- /* TODO: should do this only once per key!!! */
- for (n=0; n < 32; n += 2) {
- key[n/2] = (hex_ascii_to_binary(k[n]) << 4) + hex_ascii_to_binary(k[n+1]);
+ else {
+ /* An algorithm we don't support at all! */
+ return tvb;
}
- /* Set CTR */
- memset(ctr_block, 0, 16);
- /* Only first 5 bytes set */
- ctr_block[0] = (pdu_security_settings->count & 0xff000000) >> 24;
- ctr_block[1] = (pdu_security_settings->count & 0x00ff0000) >> 16;
- ctr_block[2] = (pdu_security_settings->count & 0x0000ff00) >> 8;
- ctr_block[3] = (pdu_security_settings->count & 0x000000ff);
- ctr_block[4] = (pdu_security_settings->bearer << 3) + (pdu_security_settings->direction << 2);
-
- /* Open gcrypt handle */
- gcrypt_err = gcry_cipher_open(&cypher_hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, 0);
- if (gcrypt_err != 0) {
+ /* Don't decipher if turned off in preferences */
+ if (((plane == SIGNALING_PLANE) && !global_pdcp_decipher_signalling) ||
+ ((plane == USER_PLANE) && !global_pdcp_decipher_userplane)) {
return tvb;
}
- /* Set the key */
- gcrypt_err = gcry_cipher_setkey(cypher_hd, key, 16);
- if (gcrypt_err != 0) {
+ /* Don't decipher control messages */
+ if ((plane == USER_PLANE) && ((tvb_get_guint8(tvb, 0) & 0x80) == 0x00)) {
return tvb;
}
- /* Set the CTR */
- gcrypt_err = gcry_cipher_setctr(cypher_hd, ctr_block, 16);
- if (gcrypt_err != 0) {
+ /* Don't decipher if not yet past SecurityModeResponse */
+ if (!will_be_deciphered) {
return tvb;
}
- /* Extract the encrypted data into a buffer */
- payload_length = tvb_length_remaining(tvb, *offset);
- encrypted_data = (guint8 *)g_malloc0(payload_length);
- tvb_memcpy(tvb, encrypted_data, *offset, payload_length);
+#ifdef HAVE_LIBGCRYPT
+ /* AES */
+ if (pdu_security_settings->ciphering == eea2) {
+ unsigned char ctr_block[16];
+ gcry_cipher_hd_t cypher_hd;
+ int gcrypt_err;
+
+ /* Set CTR */
+ memset(ctr_block, 0, 16);
+ /* Only first 5 bytes set */
+ ctr_block[0] = (pdu_security_settings->count & 0xff000000) >> 24;
+ ctr_block[1] = (pdu_security_settings->count & 0x00ff0000) >> 16;
+ ctr_block[2] = (pdu_security_settings->count & 0x0000ff00) >> 8;
+ ctr_block[3] = (pdu_security_settings->count & 0x000000ff);
+ ctr_block[4] = (pdu_security_settings->bearer << 3) + (pdu_security_settings->direction << 2);
+
+ /* Open gcrypt handle */
+ gcrypt_err = gcry_cipher_open(&cypher_hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, 0);
+ if (gcrypt_err != 0) {
+ return tvb;
+ }
- /* Allocate memory to receive decrypted payload */
- decrypted_data = (guint8 *)g_malloc0(payload_length);
+ /* Set the key */
+ gcrypt_err = gcry_cipher_setkey(cypher_hd, pdu_security_settings->cipherKey, 16);
+ if (gcrypt_err != 0) {
+ gcry_cipher_close(cypher_hd);
+ return tvb;
+ }
- /* Decrypt the actual data */
- gcrypt_err = gcry_cipher_decrypt(cypher_hd,
- decrypted_data, payload_length,
- encrypted_data, payload_length);
- if (gcrypt_err != 0) {
- return tvb;
+ /* Set the CTR */
+ gcrypt_err = gcry_cipher_setctr(cypher_hd, ctr_block, 16);
+ if (gcrypt_err != 0) {
+ gcry_cipher_close(cypher_hd);
+ return tvb;
+ }
+
+ /* Extract the encrypted data into a buffer */
+ payload_length = tvb_captured_length_remaining(tvb, *offset);
+ decrypted_data = (guint8 *)g_malloc0(payload_length);
+ tvb_memcpy(tvb, decrypted_data, *offset, payload_length);
+
+ /* Decrypt the actual data */
+ gcrypt_err = gcry_cipher_decrypt(cypher_hd,
+ decrypted_data, payload_length,
+ NULL, 0);
+ if (gcrypt_err != 0) {
+ gcry_cipher_close(cypher_hd);
+ g_free(decrypted_data);
+ return tvb;
+ }
+
+ /* Close gcrypt handle */
+ gcry_cipher_close(cypher_hd);
}
+#endif
+
+#ifdef HAVE_SNOW3G
+ /* SNOW-3G */
+ if (pdu_security_settings->ciphering == eea1) {
+ /* Extract the encrypted data into a buffer */
+ payload_length = tvb_captured_length_remaining(tvb, *offset);
+ decrypted_data = (guint8 *)g_malloc0(payload_length+4);
+ tvb_memcpy(tvb, decrypted_data, *offset, payload_length);
+
+ /* Do the algorithm */
+ snow3g_f8(pdu_security_settings->cipherKey,
+ pdu_security_settings->count,
+ pdu_security_settings->bearer,
+ pdu_security_settings->direction,
+ decrypted_data, payload_length*8);
+ }
+#endif
/* Create tvb for resulting deciphered sdu */
decrypted_tvb = tvb_new_child_real_data(tvb, decrypted_data, payload_length, payload_length);
tvb_set_free_cb(decrypted_tvb, g_free);
add_new_data_source(pinfo, decrypted_tvb, "Deciphered Payload");
- /* Free temp buffer */
- g_free(encrypted_data);
-
/* Return deciphered data, i.e. beginning of new tvb */
*offset = 0;
*deciphered = TRUE;
return decrypted_tvb;
}
-#else
-tvbuff_t* decipher_payload(tvbuff_t *tvb, packet_info *pinfo _U_, int *offset _U_, pdu_security_settings_t *pdu_security_settings _U_,
- gboolean *deciphered _U_)
+
+
+/* Try to calculate digest to compare with that found in frame. */
+static guint32 calculate_digest(pdu_security_settings_t *pdu_security_settings, guint8 header _U_,
+ tvbuff_t *tvb _U_, gint offset _U_, gboolean *calculated)
{
- return tvb;
-}
+ *calculated = FALSE;
+
+ if (pdu_security_settings->integrity == eia0) {
+ /* Should be zero in this case */
+ *calculated = TRUE;
+ return 0;
+ }
+
+ /* Can't calculate if don't have valid integrity key */
+ if (!pdu_security_settings->integrityKeyValid) {
+ return 0;
+ }
+
+ /* Can only do if indicated in preferences */
+ if (!global_pdcp_check_integrity) {
+ return 0;
+ }
+
+ switch (pdu_security_settings->integrity) {
+
+#ifdef HAVE_SNOW3G
+ case eia1:
+ {
+ guint8 *mac;
+ gint message_length = tvb_captured_length_remaining(tvb, offset) - 4;
+ guint8 *message_data = (guint8 *)g_malloc0(message_length+5);
+ message_data[0] = header;
+ tvb_memcpy(tvb, message_data+1, offset, message_length);
+
+ mac = (u8*)snow3g_f9(pdu_security_settings->integrityKey,
+ pdu_security_settings->count,
+ /* 'Fresh' is the bearer bits then zeros */
+ pdu_security_settings->bearer << 27,
+ pdu_security_settings->direction,
+ message_data,
+ (message_length+1)*8);
+
+ *calculated = TRUE;
+ g_free(message_data);
+ return ((mac[0] << 24) | (mac[1] << 16) | (mac[2] << 8) | mac[3]);
+ }
#endif
+#if (defined GCRYPT_VERSION_NUMBER) && (GCRYPT_VERSION_NUMBER >= 0x010600)
+ case eia2:
+ {
+ gcry_mac_hd_t mac_hd;
+ int gcrypt_err;
+ gint message_length;
+ guint8 *message_data;
+ guint8 mac[4];
+ size_t read_digest_length = 4;
+
+ /* Open gcrypt handle */
+ gcrypt_err = gcry_mac_open(&mac_hd, GCRY_MAC_CMAC_AES, 0, NULL);
+ if (gcrypt_err != 0) {
+ return 0;
+ }
+
+ /* Set the key */
+ gcrypt_err = gcry_mac_setkey(mac_hd, pdu_security_settings->integrityKey, 16);
+ if (gcrypt_err != 0) {
+ gcry_mac_close(mac_hd);
+ return 0;
+ }
+
+ /* Extract the encrypted data into a buffer */
+ message_length = tvb_captured_length_remaining(tvb, offset) - 4;
+ message_data = (guint8 *)g_malloc0(message_length+9);
+ message_data[0] = (pdu_security_settings->count & 0xff000000) >> 24;
+ message_data[1] = (pdu_security_settings->count & 0x00ff0000) >> 16;
+ message_data[2] = (pdu_security_settings->count & 0x0000ff00) >> 8;
+ message_data[3] = (pdu_security_settings->count & 0x000000ff);
+ message_data[4] = (pdu_security_settings->bearer << 3) + (pdu_security_settings->direction << 2);
+ /* rest of first 8 bytes are left as zeroes... */
+ message_data[8] = header;
+ tvb_memcpy(tvb, message_data+9, offset, message_length);
+
+ /* Pass in the message */
+ gcrypt_err = gcry_mac_write(mac_hd, message_data, message_length+9);
+ if (gcrypt_err != 0) {
+ gcry_mac_close(mac_hd);
+ g_free(message_data);
+ return 0;
+ }
+
+ /* Read out the digest */
+ gcrypt_err = gcry_mac_read(mac_hd, mac, &read_digest_length);
+ if (gcrypt_err != 0) {
+ gcry_mac_close(mac_hd);
+ g_free(message_data);
+ return 0;
+ }
+
+ /* Now close the mac handle */
+ gcry_mac_close(mac_hd);
+
+ g_free(message_data);
+
+ *calculated = TRUE;
+ return ((mac[0] << 24) | (mac[1] << 16) | (mac[2] << 8) | mac[3]);
+ }
+#endif
+
+ default:
+ /* Can't calculate */
+ *calculated = FALSE;
+ return 0;
+ }
+}
+
+
/******************************/
/* Main dissection function. */
proto_tree *pdcp_tree = NULL;
proto_item *root_ti = NULL;
gint offset = 0;
- gint rohc_offset;
struct pdcp_lte_info *p_pdcp_info;
tvbuff_t *rohc_tvb = NULL;
gboolean payload_deciphered = FALSE;
/* Initialise security settings */
- pdu_security_settings.valid = FALSE;
- pdu_security_settings.key = "";
-
+ memset(&pdu_security_settings, 0, sizeof(pdu_security_settings));
/* Set protocol name. */
col_set_str(pinfo->cinfo, COL_PROTOCOL, "PDCP-LTE");
if (!pinfo->fd->flags.visited) {
/* Look up current state by UEID */
current_security = (pdcp_security_info_t*)g_hash_table_lookup(pdcp_security_hash,
- GUINT_TO_POINTER((guint)p_pdcp_info->ueid));
+ GUINT_TO_POINTER((guint)p_pdcp_info->ueid));
if (current_security != NULL) {
/* Store any result for this frame in the result table */
pdcp_security_info_t *security_to_store = wmem_new(wmem_file_scope(), pdcp_security_info_t);
+ /* Take a deep copy of the settings */
*security_to_store = *current_security;
g_hash_table_insert(pdcp_security_result_hash,
get_ueid_frame_hash_key(p_pdcp_info->ueid, pinfo->fd->num, TRUE),
security_to_store);
}
+ else {
+ /* No entry added from RRC, but still use configured defaults */
+ if ((global_default_ciphering_algorithm != eea0) ||
+ (global_default_integrity_algorithm != eia0)) {
+ /* Copy algorithms from preference defaults */
+ pdcp_security_info_t *security_to_store = wmem_new0(wmem_file_scope(), pdcp_security_info_t);
+ security_to_store->ciphering = global_default_ciphering_algorithm;
+ security_to_store->integrity = global_default_integrity_algorithm;
+ security_to_store->seen_next_ul_pdu = TRUE;
+ g_hash_table_insert(pdcp_security_result_hash,
+ get_ueid_frame_hash_key(p_pdcp_info->ueid, pinfo->fd->num, TRUE),
+ security_to_store);
+ }
+ }
}
/* Show security settings for this PDU */
val_to_str_const(pdu_security->ciphering, ciphering_algorithm_vals, "Unknown"),
val_to_str_const(pdu_security->integrity, integrity_algorithm_vals, "Unknown"));
- pdu_security_settings.valid = TRUE;
pdu_security_settings.ciphering = pdu_security->ciphering;
+ pdu_security_settings.integrity = pdu_security->integrity;
}
write_pdu_label_and_info(root_ti, pinfo, " sn=%-2u ", seqnum);
offset++;
- if (tvb_length_remaining(tvb, offset) == 0) {
+ if (tvb_captured_length_remaining(tvb, offset) == 0) {
/* Only PDCP header was captured, stop dissection here */
return;
}
}
/* Bitmap tree */
- if (tvb_length_remaining(tvb, offset) > 0) {
+ if (tvb_reported_length_remaining(tvb, offset) > 0) {
bitmap_ti = proto_tree_add_item(pdcp_tree, hf_pdcp_lte_bitmap, tvb,
offset, -1, ENC_NA);
bitmap_tree = proto_item_add_subtree(bitmap_ti, ett_pdcp_report_bitmap);
buff = (gchar *)wmem_alloc(wmem_packet_scope(), BUFF_SIZE);
- len = tvb_length_remaining(tvb, offset);
+ len = tvb_reported_length_remaining(tvb, offset);
bit_offset = offset<<3;
/* For each byte... */
for (i=0; i<len; i++) {
not_received++;
}
}
- proto_tree_add_text(bitmap_tree, tvb, bit_offset/8, 1, "%s", buff);
+ proto_tree_add_uint_format(bitmap_tree, hf_pdcp_lte_bitmap_byte, tvb, bit_offset/8, 1, bits, "%s", buff);
bit_offset += 8;
}
}
/* Check pdu_security_settings - may need to do deciphering before calling
further dissectors on payload */
- payload_tvb = decipher_payload(tvb, pinfo, &offset, &pdu_security_settings, p_pdcp_info->plane, &payload_deciphered);
+ payload_tvb = decipher_payload(tvb, pinfo, &offset, &pdu_security_settings, p_pdcp_info->plane,
+ pdu_security ? pdu_security->seen_next_ul_pdu: FALSE, &payload_deciphered);
if (p_pdcp_info->plane == SIGNALING_PLANE) {
guint32 data_length;
guint32 mac;
+ proto_item *mac_ti;
+ guint32 calculated_digest = 0;
+ gboolean digest_was_calculated = FALSE;
+
+ /* Try to calculate digest so we can check it */
+ if (global_pdcp_check_integrity) {
+ calculated_digest = calculate_digest(&pdu_security_settings, tvb_get_guint8(tvb, 0), payload_tvb,
+ offset, &digest_was_calculated);
+ }
/* RRC data is all but last 4 bytes.
- Call lte-rrc dissector (according to direction and channel type) */
+ Call lte-rrc dissector (according to direction and channel type) if we have valid data */
if ((global_pdcp_dissect_signalling_plane_as_rrc) &&
- ((pdu_security == NULL) || (pdu_security->ciphering == 0) || payload_deciphered || !pdu_security->seen_next_ul_pdu)){
+ ((pdu_security == NULL) || (pdu_security->ciphering == eea0) || payload_deciphered || !pdu_security->seen_next_ul_pdu)) {
/* Get appropriate dissector handle */
dissector_handle_t rrc_handle = lookup_rrc_dissector_handle(p_pdcp_info);
if (rrc_handle != 0) {
/* Call RRC dissector if have one */
tvbuff_t *rrc_payload_tvb = tvb_new_subset(payload_tvb, offset,
- tvb_length_remaining(payload_tvb, offset) - 4,
- tvb_length_remaining(payload_tvb, offset) - 4);
+ tvb_captured_length_remaining(payload_tvb, offset) - 4,
+ tvb_reported_length_remaining(payload_tvb, offset) - 4);
gboolean was_writable = col_get_writable(pinfo->cinfo);
/* We always want to see this in the info column */
else {
/* Just show data */
proto_tree_add_item(pdcp_tree, hf_pdcp_lte_signalling_data, payload_tvb, offset,
- tvb_length_remaining(tvb, offset) - 4, ENC_NA);
+ tvb_reported_length_remaining(tvb, offset) - 4, ENC_NA);
}
if (!pinfo->fd->flags.visited &&
else {
/* Just show as unparsed data */
proto_tree_add_item(pdcp_tree, hf_pdcp_lte_signalling_data, payload_tvb, offset,
- tvb_length_remaining(tvb, offset) - 4, ENC_NA);
+ tvb_reported_length_remaining(tvb, offset) - 4, ENC_NA);
}
- data_length = tvb_length_remaining(payload_tvb, offset) - 4;
+ data_length = tvb_reported_length_remaining(payload_tvb, offset) - 4;
offset += data_length;
/* Last 4 bytes are MAC */
- mac = tvb_get_ntohl(tvb, offset);
- proto_tree_add_item(pdcp_tree, hf_pdcp_lte_mac, payload_tvb, offset, 4, ENC_BIG_ENDIAN);
+ mac = tvb_get_ntohl(payload_tvb, offset);
+ mac_ti = proto_tree_add_item(pdcp_tree, hf_pdcp_lte_mac, payload_tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
+ if (digest_was_calculated) {
+ /* Compare what was found with calculated value! */
+ if (mac != calculated_digest) {
+ expert_add_info_format(pinfo, mac_ti, &ei_pdcp_lte_digest_wrong,
+ "MAC-I Digest wrong expected %08x but found %08x",
+ calculated_digest, mac);
+ }
+ else {
+ proto_item_append_text(mac_ti, " [Matches calculated result]");
+ }
+ }
+
col_append_fstr(pinfo->cinfo, COL_INFO, " MAC=0x%08x (%u bytes data)",
mac, data_length);
}
- else {
+ else if (tvb_captured_length_remaining(payload_tvb, offset)) {
/* User-plane payload here */
/* If not compressed with ROHC, show as user-plane data */
if (!p_pdcp_info->rohc.rohc_compression) {
- gint payload_length = tvb_length_remaining(payload_tvb, offset);
+ gint payload_length = tvb_reported_length_remaining(payload_tvb, offset);
if (payload_length > 0) {
if (p_pdcp_info->plane == USER_PLANE) {
/* Not attempting to decode payload if ciphering is enabled
(and NULL ciphering is not being used) */
if (global_pdcp_dissect_user_plane_as_ip &&
- ((pdu_security == NULL) || (pdu_security->ciphering == 0) || payload_deciphered))
+ ((pdu_security == NULL) || (pdu_security->ciphering == eea0) || payload_deciphered))
{
tvbuff_t *ip_payload_tvb = tvb_new_subset_remaining(payload_tvb, offset);
col_set_writable(pinfo->cinfo, FALSE);
}
- switch (tvb_get_guint8(ip_payload_tvb, offset) & 0xf0) {
+ switch (tvb_get_guint8(ip_payload_tvb, 0) & 0xf0) {
case 0x40:
call_dissector_only(ip_handle, ip_payload_tvb, pinfo, pdcp_tree, NULL);
break;
return;
}
- rohc_offset = offset;
- rohc_tvb = tvb_new_subset_remaining(payload_tvb, rohc_offset);
+ rohc_tvb = tvb_new_subset_remaining(payload_tvb, offset);
/* Only enable writing to column if configured to show ROHC */
if (global_pdcp_lte_layer_to_show != ShowTrafficLayer) {
* file is loaded or re-loaded in wireshark */
static void pdcp_lte_init_protocol(void)
{
- /* Destroy any existing hashes. */
- if (pdcp_sequence_analysis_channel_hash) {
- g_hash_table_destroy(pdcp_sequence_analysis_channel_hash);
- }
- if (pdcp_lte_sequence_analysis_report_hash) {
- g_hash_table_destroy(pdcp_lte_sequence_analysis_report_hash);
- }
- if (pdcp_security_hash) {
- g_hash_table_destroy(pdcp_security_hash);
- }
- if (pdcp_security_result_hash) {
- g_hash_table_destroy(pdcp_security_result_hash);
- }
-
- /* Now create them over */
- pdcp_sequence_analysis_channel_hash = g_hash_table_new(pdcp_channel_hash_func, pdcp_channel_equal);
+ pdcp_sequence_analysis_channel_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
pdcp_lte_sequence_analysis_report_hash = g_hash_table_new(pdcp_result_hash_func, pdcp_result_hash_equal);
pdcp_security_hash = g_hash_table_new(pdcp_lte_ueid_hash_func, pdcp_lte_ueid_hash_equal);
pdcp_security_result_hash = g_hash_table_new(pdcp_lte_ueid_frame_hash_func, pdcp_lte_ueid_frame_hash_equal);
+ pdcp_security_key_hash = g_hash_table_new(pdcp_lte_ueid_hash_func, pdcp_lte_ueid_hash_equal);
+}
+
+static void pdcp_lte_cleanup_protocol(void)
+{
+ g_hash_table_destroy(pdcp_sequence_analysis_channel_hash);
+ g_hash_table_destroy(pdcp_lte_sequence_analysis_report_hash);
+ g_hash_table_destroy(pdcp_security_hash);
+ g_hash_table_destroy(pdcp_security_result_hash);
+ g_hash_table_destroy(pdcp_security_key_hash);
}
},
{ &hf_pdcp_lte_mac,
{ "MAC",
- "pdcp-lte.mac", FT_UINT32, BASE_HEX_DEC, NULL, 0x0,
+ "pdcp-lte.mac", FT_UINT32, BASE_HEX, NULL, 0x0,
NULL, HFILL
}
},
"Status report bitmap (0=error, 1=OK)", HFILL
}
},
-
+ { &hf_pdcp_lte_bitmap_byte,
+ { "Bitmap byte",
+ "pdcp-lte.bitmap.byte", FT_UINT8, BASE_HEX, NULL, 0x0,
+ NULL, HFILL
+ }
+ },
{ &hf_pdcp_lte_sequence_analysis,
{ "Sequence Analysis",
NULL, HFILL
}
},
- { &hf_pdcp_lte_security_key,
- { "KEY",
- "pdcp-lte.security-config.key", FT_STRING, BASE_NONE, NULL, 0x0,
+ { &hf_pdcp_lte_security_cipher_key,
+ { "CIPHER KEY",
+ "pdcp-lte.security-config.cipher-key", FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL
+ }
+ },
+ { &hf_pdcp_lte_security_integrity_key,
+ { "INTEGRITY KEY",
+ "pdcp-lte.security-config.integrity-key", FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL
}
},
{ &ei_pdcp_lte_sequence_analysis_sn_repeated, { "pdcp-lte.sequence-analysis.sn-repeated", PI_SEQUENCE, PI_WARN, "PDCP SN repeated", EXPFILL }},
{ &ei_pdcp_lte_sequence_analysis_wrong_sequence_number, { "pdcp-lte.sequence-analysis.wrong-sequence-number", PI_SEQUENCE, PI_WARN, "Wrong Sequence Number", EXPFILL }},
{ &ei_pdcp_lte_reserved_bits_not_zero, { "pdcp-lte.reserved-bits-not-zero", PI_MALFORMED, PI_ERROR, "Reserved bits not zero", EXPFILL }},
+ { &ei_pdcp_lte_digest_wrong, { "pdcp-lte.maci-wrong", PI_SEQUENCE, PI_ERROR, "MAC-I doesn't match expected value", EXPFILL }}
};
static const enum_val_t sequence_analysis_vals[] = {
{NULL, NULL, -1}
};
-#ifdef HAVE_LIBGCRYPT
+ static const enum_val_t default_ciphering_algorithm_vals[] = {
+ {"eea0", "EEA0 (NULL)", eea0},
+ {"eea1", "EEA1 (SNOW3G)", eea1},
+ {"eea2", "EEA2 (AES)", eea2},
+ {"eea3", "EEA3 (ZUC)", eea3},
+ {NULL, NULL, -1}
+ };
+
+ static const enum_val_t default_integrity_algorithm_vals[] = {
+ {"eia0", "EIA0 (NULL)", eia0},
+ {"eia1", "EIA1 (SNOW3G)", eia1},
+ {"eia2", "EIA2 (AES)", eia2},
+ {"eia3", "EIA3 (ZUC)", eia3},
+ {NULL, NULL, -1}
+ };
+
static uat_field_t ue_keys_uat_flds[] = {
UAT_FLD_DEC(uat_ue_keys_records, ueid, "UEId", "UE Identifier of UE associated with keys"),
- UAT_FLD_CSTRING(uat_ue_keys_records, rrcKey, "RRC Key", "Key for deciphering signalling messages"),
- UAT_FLD_CSTRING(uat_ue_keys_records, upKey, "User-Plane Key", "Key for deciphering user-plane messages"),
+ UAT_FLD_CSTRING(uat_ue_keys_records, rrcCipherKeyString, "RRC Cipher Key", "Key for deciphering signalling messages"),
+ UAT_FLD_CSTRING(uat_ue_keys_records, upCipherKeyString, "User-Plane Cipher Key", "Key for deciphering user-plane messages"),
+ UAT_FLD_CSTRING(uat_ue_keys_records, rrcIntegrityKeyString, "RRC Integrity Key", "Key for deciphering user-plane messages"),
UAT_END_FIELDS
};
-#endif
module_t *pdcp_lte_module;
expert_module_t* expert_pdcp_lte;
"Attempt to decode ROHC data",
&global_pdcp_dissect_rohc);
- prefs_register_bool_preference(pdcp_lte_module, "heuristic_pdcp_lte_over_udp",
- "Try Heuristic LTE-PDCP over UDP framing",
- "When enabled, use heuristic dissector to find PDCP-LTE frames sent with "
- "UDP framing",
- &global_pdcp_lte_heur);
+ prefs_register_obsolete_preference(pdcp_lte_module, "heuristic_pdcp_lte_over_udp");
prefs_register_enum_preference(pdcp_lte_module, "layer_to_show",
"Which layer info to show in Info column",
"Can show RLC, PDCP or Traffic layer info in Info column",
&global_pdcp_lte_layer_to_show, show_info_col_vals, FALSE);
-#ifdef HAVE_LIBGCRYPT
ue_keys_uat = uat_new("PDCP UE security keys",
sizeof(uat_ue_keys_record_t), /* record size */
"pdcp_lte_ue_keys", /* filename */
TRUE, /* from_profile */
- (void**) &uat_ue_keys_records, /* data_ptr */
+ &uat_ue_keys_records, /* data_ptr */
&num_ue_keys_uat, /* numitems_ptr */
UAT_AFFECTS_DISSECTION, /* affects dissection of packets, but not set of named fields */
NULL, /* help */
uat_ue_keys_record_copy_cb, /* copy callback */
- NULL, /* update callback */
+ uat_ue_keys_record_update_cb, /* update callback */
uat_ue_keys_record_free_cb, /* free callback */
NULL, /* post update callback */
ue_keys_uat_flds); /* UAT field definitions */
"Preconfigured PDCP keys",
ue_keys_uat);
+ prefs_register_enum_preference(pdcp_lte_module, "default_ciphering_algorithm",
+ "Ciphering algorithm to use if not signalled",
+ "If RRC Security Info not seen, e.g. in Handover",
+ (gint*)&global_default_ciphering_algorithm, default_ciphering_algorithm_vals, FALSE);
+
+ prefs_register_enum_preference(pdcp_lte_module, "default_integrity_algorithm",
+ "Integrity algorithm to use if not signalled",
+ "If RRC Security Info not seen, e.g. in Handover",
+ (gint*)&global_default_integrity_algorithm, default_integrity_algorithm_vals, FALSE);
+
/* Attempt to decipher RRC messages */
prefs_register_bool_preference(pdcp_lte_module, "decipher_signalling",
"Attempt to decipher Signalling (RRC) SDUs",
- "N.B. only possible if key available and configured",
+ "N.B. only possible if build with algorithm support, and have key available and configured",
&global_pdcp_decipher_signalling);
/* Attempt to decipher user-plane messages */
prefs_register_bool_preference(pdcp_lte_module, "decipher_userplane",
"Attempt to decipher User-plane (IP) SDUs",
- "N.B. only possible if key available and configured",
+ "N.B. only possible if build with algorithm support, and have key available and configured",
&global_pdcp_decipher_userplane);
-#endif
+
+ /* Attempt to verify RRC integrity/authentication digest */
+ prefs_register_bool_preference(pdcp_lte_module, "verify_integrity",
+ "Attempt to check integrity calculation",
+ "N.B. only possible if build with algorithm support, and have key available and configured",
+ &global_pdcp_check_integrity);
register_init_routine(&pdcp_lte_init_protocol);
+ register_cleanup_routine(&pdcp_lte_cleanup_protocol);
}
void proto_reg_handoff_pdcp_lte(void)
{
/* Add as a heuristic UDP dissector */
- heur_dissector_add("udp", dissect_pdcp_lte_heur, proto_pdcp_lte);
+ heur_dissector_add("udp", dissect_pdcp_lte_heur, "PDCP-LTE over UDP", "pdcp_lte_udp", proto_pdcp_lte, HEURISTIC_DISABLE);
ip_handle = find_dissector("ip");
ipv6_handle = find_dissector("ipv6");